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 */ 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 */ 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 */ 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 */ 107 public abstract Bundle validateAccount(HostAuth hostAuth, Context context); 108 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 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 */ 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 155 ValidationResult(boolean _success, int _failure, String _reason) { 156 success = _success; 157 failure = _failure; 158 reason = _reason; 159 } 160 161 ValidationResult(boolean _success) { 162 success = _success; 163 } 164 165 ValidationResult(Exception e) { 166 success = false; 167 failure = EXCEPTION; 168 exception = e; 169 } 170 171 public boolean isSuccess() { 172 return success; 173 } 174 175 public String getReason() { 176 return reason; 177 } 178 } 179 180 public boolean isStopped() { 181 return mStop; 182 } 183 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 */ 192 public void userLog(String string, int code, String string2) { 193 if (Eas.USER_LOG) { 194 userLog(string + code + string2); 195 } 196 } 197 198 public void userLog(String string, int code) { 199 if (Eas.USER_LOG) { 200 userLog(string + code); 201 } 202 } 203 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 */ 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 */ 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 */ 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 281 public void addRequest(Request req) { 282 mRequestQueue.offer(req); 283 } 284 285 public void removeRequest(Request req) { 286 mRequestQueue.remove(req); 287 } 288 289 public boolean hasPendingRequests() { 290 return !mRequestQueue.isEmpty(); 291 } 292 293 public void clearRequests() { 294 mRequestQueue.clear(); 295 } 296 } 297