• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008-2009 Marc Blank
3  * Licensed to The Android Open Source Project.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.exchange;
19 
20 import com.android.emailcommon.provider.Account;
21 import com.android.emailcommon.provider.HostAuth;
22 import com.android.emailcommon.provider.Mailbox;
23 import com.android.exchange.utility.FileLogger;
24 
25 import android.content.Context;
26 import android.net.ConnectivityManager;
27 import android.net.NetworkInfo;
28 import android.net.NetworkInfo.DetailedState;
29 import android.os.Bundle;
30 import android.util.Log;
31 
32 import java.util.concurrent.LinkedBlockingQueue;
33 
34 /**
35  * Base class for all protocol services SyncManager (extends Service, implements
36  * Runnable) instantiates subclasses to run a sync (either timed, or push, or
37  * mail placed in outbox, etc.) EasSyncService is currently implemented; my goal
38  * would be to move IMAP to this structure when it comes time to introduce push
39  * functionality.
40  */
41 public abstract class AbstractSyncService implements Runnable {
42 
43     public String TAG = "AbstractSyncService";
44 
45     public static final int SECONDS = 1000;
46     public static final int MINUTES = 60*SECONDS;
47     public static final int HOURS = 60*MINUTES;
48     public static final int DAYS = 24*HOURS;
49 
50     public static final int CONNECT_TIMEOUT = 30*SECONDS;
51     public static final int NETWORK_WAIT = 15*SECONDS;
52 
53     public static final String EAS_PROTOCOL = "eas";
54     public static final int EXIT_DONE = 0;
55     public static final int EXIT_IO_ERROR = 1;
56     public static final int EXIT_LOGIN_FAILURE = 2;
57     public static final int EXIT_EXCEPTION = 3;
58     public static final int EXIT_SECURITY_FAILURE = 4;
59     public static final int EXIT_ACCESS_DENIED = 5;
60 
61     public Mailbox mMailbox;
62     protected long mMailboxId;
63     protected int mExitStatus = EXIT_EXCEPTION;
64     protected String mMailboxName;
65     public Account mAccount;
66     public Context mContext;
67     public int mChangeCount = 0;
68     public volatile int mSyncReason = 0;
69     protected volatile boolean mStop = false;
70     protected volatile Thread mThread;
71     protected final Object mSynchronizer = new Object();
72 
73     protected volatile long mRequestTime = 0;
74     protected LinkedBlockingQueue<Request> mRequestQueue = new LinkedBlockingQueue<Request>();
75 
76     /**
77      * Sent by SyncManager to request that the service stop itself cleanly
78      */
stop()79     public abstract void stop();
80 
81     /**
82      * Sent by SyncManager to indicate that an alarm has fired for this service, and that its
83      * pending (network) operation has timed out. The service is NOT automatically stopped,
84      * although the behavior is service dependent.
85      *
86      * @return true if the operation was stopped normally; false if the thread needed to be
87      * interrupted.
88      */
alarm()89     public abstract boolean alarm();
90 
91     /**
92      * Sent by SyncManager to request that the service reset itself cleanly; the meaning of this
93      * operation is service dependent.
94      */
reset()95     public abstract void reset();
96 
97     /**
98      * Called to validate an account; abstract to allow each protocol to do what
99      * is necessary. For consistency with the Email app's original
100      * functionality, success is indicated by a failure to throw an Exception
101      * (ugh). Parameters are self-explanatory
102      *
103      * @param hostAuth
104      * @return a Bundle containing a result code and, depending on the result, a PolicySet or an
105      * error message
106      */
validateAccount(HostAuth hostAuth, Context context)107     public abstract Bundle validateAccount(HostAuth hostAuth, Context context);
108 
AbstractSyncService(Context _context, Mailbox _mailbox)109     public AbstractSyncService(Context _context, Mailbox _mailbox) {
110         mContext = _context;
111         mMailbox = _mailbox;
112         mMailboxId = _mailbox.mId;
113         mMailboxName = _mailbox.mServerId;
114         mAccount = Account.restoreAccountWithId(_context, _mailbox.mAccountKey);
115     }
116 
117     // Will be required when subclasses are instantiated by name
AbstractSyncService(String prefix)118     public AbstractSyncService(String prefix) {
119     }
120 
121     /**
122      * The UI can call this static method to perform account validation.  This method wraps each
123      * protocol's validateAccount method.   Arguments are self-explanatory, except where noted.
124      *
125      * @param klass the protocol class (EasSyncService.class for example)
126      * @param hostAuth
127      * @param context
128      * @return a Bundle containing a result code and, depending on the result, a PolicySet or an
129      * error message
130      */
validate(Class<? extends AbstractSyncService> klass, HostAuth hostAuth, Context context)131     public static Bundle validate(Class<? extends AbstractSyncService> klass,
132             HostAuth hostAuth, Context context) {
133         AbstractSyncService svc;
134         try {
135             svc = klass.newInstance();
136             return svc.validateAccount(hostAuth, context);
137         } catch (IllegalAccessException e) {
138         } catch (InstantiationException e) {
139         }
140         return null;
141     }
142 
143     public static class ValidationResult {
144         static final int NO_FAILURE = 0;
145         static final int CONNECTION_FAILURE = 1;
146         static final int VALIDATION_FAILURE = 2;
147         static final int EXCEPTION = 3;
148 
149         static final ValidationResult succeeded = new ValidationResult(true, NO_FAILURE, null);
150         boolean success;
151         int failure = NO_FAILURE;
152         String reason = null;
153         Exception exception = null;
154 
ValidationResult(boolean _success, int _failure, String _reason)155         ValidationResult(boolean _success, int _failure, String _reason) {
156             success = _success;
157             failure = _failure;
158             reason = _reason;
159         }
160 
ValidationResult(boolean _success)161         ValidationResult(boolean _success) {
162             success = _success;
163         }
164 
ValidationResult(Exception e)165         ValidationResult(Exception e) {
166             success = false;
167             failure = EXCEPTION;
168             exception = e;
169         }
170 
isSuccess()171         public boolean isSuccess() {
172             return success;
173         }
174 
getReason()175         public String getReason() {
176             return reason;
177         }
178     }
179 
isStopped()180     public boolean isStopped() {
181         return mStop;
182     }
183 
getSynchronizer()184     public Object getSynchronizer() {
185         return mSynchronizer;
186     }
187 
188     /**
189      * Convenience methods to do user logging (i.e. connection activity).  Saves a bunch of
190      * repetitive code.
191      */
userLog(String string, int code, String string2)192     public void userLog(String string, int code, String string2) {
193         if (Eas.USER_LOG) {
194             userLog(string + code + string2);
195         }
196     }
197 
userLog(String string, int code)198     public void userLog(String string, int code) {
199         if (Eas.USER_LOG) {
200             userLog(string + code);
201         }
202     }
203 
userLog(String str, Exception e)204     public void userLog(String str, Exception e) {
205         if (Eas.USER_LOG) {
206             Log.e(TAG, str, e);
207         } else {
208             Log.e(TAG, str + e);
209         }
210         if (Eas.FILE_LOG) {
211             FileLogger.log(e);
212         }
213     }
214 
215     /**
216      * Standard logging for EAS.
217      * If user logging is active, we concatenate any arguments and log them using Log.d
218      * We also check for file logging, and log appropriately
219      * @param strings strings to concatenate and log
220      */
userLog(String ....strings)221     public void userLog(String ...strings) {
222         if (Eas.USER_LOG) {
223             String logText;
224             if (strings.length == 1) {
225                 logText = strings[0];
226             } else {
227                 StringBuilder sb = new StringBuilder(64);
228                 for (String string: strings) {
229                     sb.append(string);
230                 }
231                 logText = sb.toString();
232             }
233             Log.d(TAG, logText);
234             if (Eas.FILE_LOG) {
235                 FileLogger.log(TAG, logText);
236             }
237         }
238     }
239 
240     /**
241      * Error log is used for serious issues that should always be logged
242      * @param str the string to log
243      */
errorLog(String str)244     public void errorLog(String str) {
245         Log.e(TAG, str);
246         if (Eas.FILE_LOG) {
247             FileLogger.log(TAG, str);
248         }
249     }
250 
251     /**
252      * Waits for up to 10 seconds for network connectivity; returns whether or not there is
253      * network connectivity.
254      *
255      * @return whether there is network connectivity
256      */
hasConnectivity()257     public boolean hasConnectivity() {
258         ConnectivityManager cm =
259                 (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
260         int tries = 0;
261         while (tries++ < 1) {
262             // Use the same test as in ExchangeService#waitForConnectivity
263             // TODO: Create common code for this test in emailcommon
264             NetworkInfo info = cm.getActiveNetworkInfo();
265             if (info != null) {
266                 return true;
267             }
268             try {
269                 Thread.sleep(10*SECONDS);
270             } catch (InterruptedException e) {
271             }
272         }
273         return false;
274     }
275 
276     /**
277      * Request handling (common functionality)
278      * Can be overridden if desired
279      */
280 
addRequest(Request req)281     public void addRequest(Request req) {
282         mRequestQueue.offer(req);
283     }
284 
removeRequest(Request req)285     public void removeRequest(Request req) {
286         mRequestQueue.remove(req);
287     }
288 
hasPendingRequests()289     public boolean hasPendingRequests() {
290         return !mRequestQueue.isEmpty();
291     }
292 
clearRequests()293     public void clearRequests() {
294         mRequestQueue.clear();
295     }
296 }
297