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