AbstractSyncService.java revision 1403386ebffa1b6093a506a6a24db4523acdc315
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 18package com.android.exchange; 19 20import com.android.email.mail.MessagingException; 21import com.android.email.provider.EmailContent.Account; 22import com.android.email.provider.EmailContent.Mailbox; 23import com.android.exchange.utility.FileLogger; 24 25import android.content.Context; 26import android.net.ConnectivityManager; 27import android.net.NetworkInfo; 28import android.net.NetworkInfo.DetailedState; 29import android.util.Log; 30 31import java.util.ArrayList; 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 */ 40public abstract class AbstractSyncService implements Runnable { 41 42 public String TAG = "AbstractSyncService"; 43 44 public static final String SUMMARY_PROTOCOL = "_SUMMARY_"; 45 public static final String SYNCED_PROTOCOL = "_SYNCING_"; 46 public static final String MOVE_FAVORITES_PROTOCOL = "_MOVE_FAVORITES_"; 47 public static final int SECONDS = 1000; 48 public static final int MINUTES = 60*SECONDS; 49 public static final int HOURS = 60*MINUTES; 50 public static final int DAYS = 24*HOURS; 51 52 public static final int CONNECT_TIMEOUT = 30*SECONDS; 53 public static final int NETWORK_WAIT = 15*SECONDS; 54 55 public static final String IMAP_PROTOCOL = "imap"; 56 public static final String EAS_PROTOCOL = "eas"; 57 public static final int EXIT_DONE = 0; 58 public static final int EXIT_IO_ERROR = 1; 59 public static final int EXIT_LOGIN_FAILURE = 2; 60 public static final int EXIT_EXCEPTION = 3; 61 62 public Mailbox mMailbox; 63 protected long mMailboxId; 64 protected Thread mThread; 65 protected int mExitStatus = EXIT_EXCEPTION; 66 protected String mMailboxName; 67 public Account mAccount; 68 public Context mContext; 69 public int mChangeCount = 0; 70 public int mSyncReason = 0; 71 protected volatile boolean mStop = false; 72 protected Object mSynchronizer = new Object(); 73 74 protected volatile long mRequestTime = 0; 75 protected ArrayList<PartRequest> mPartRequests = new ArrayList<PartRequest>(); 76 protected PartRequest mPendingPartRequest = null; 77 78 /** 79 * Sent by SyncManager to request that the service stop itself cleanly 80 */ 81 public abstract void stop(); 82 83 /** 84 * Sent by SyncManager to indicate a user request requiring service has been 85 * added to the service's pending request queue 86 */ 87 public abstract void ping(); 88 89 /** 90 * Called to validate an account; abstract to allow each protocol to do what 91 * is necessary. For consistency with the Email app's original 92 * functionality, success is indicated by a failure to throw an Exception 93 * (ugh). Parameters are self-explanatory 94 * 95 * @param host 96 * @param userName 97 * @param password 98 * @param port 99 * @param ssl 100 * @param context 101 * @throws MessagingException 102 */ 103 public abstract void validateAccount(String host, String userName, String password, int port, 104 boolean ssl, Context context) throws MessagingException; 105 106 public AbstractSyncService(Context _context, Mailbox _mailbox) { 107 mContext = _context; 108 mMailbox = _mailbox; 109 mMailboxId = _mailbox.mId; 110 mMailboxName = _mailbox.mServerId; 111 mAccount = Account.restoreAccountWithId(_context, _mailbox.mAccountKey); 112 } 113 114 // Will be required when subclasses are instantiated by name 115 public AbstractSyncService(String prefix) { 116 } 117 118 /** 119 * The UI can call this static method to perform account validation. This method wraps each 120 * protocol's validateAccount method. Arguments are self-explanatory, except where noted. 121 * 122 * @param klass the protocol class (EasSyncService.class for example) 123 * @param host 124 * @param userName 125 * @param password 126 * @param port 127 * @param ssl 128 * @param context 129 * @throws MessagingException 130 */ 131 static public void validate(Class<? extends AbstractSyncService> klass, String host, 132 String userName, String password, int port, boolean ssl, Context context) 133 throws MessagingException { 134 AbstractSyncService svc; 135 try { 136 svc = klass.newInstance(); 137 svc.validateAccount(host, userName, password, port, ssl, context); 138 } catch (IllegalAccessException e) { 139 throw new MessagingException("internal error", e); 140 } catch (InstantiationException e) { 141 throw new MessagingException("internal error", e); 142 } 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 (Eas.USER_LOG) { 196 userLog(string + code + string2); 197 } 198 } 199 200 public void userLog(String string, int code) { 201 if (Eas.USER_LOG) { 202 userLog(string + code); 203 } 204 } 205 206 public void userLog(Exception e) { 207 if (Eas.FILE_LOG) { 208 FileLogger.log(e); 209 } 210 } 211 212 /** 213 * Standard logging for EAS. 214 * If user logging is active, we concatenate any arguments and log them using Log.d 215 * We also check for file logging, and log appropriately 216 * @param strings strings to concatenate and log 217 */ 218 public void userLog(String ...strings) { 219 if (Eas.USER_LOG) { 220 String logText; 221 if (strings.length == 1) { 222 logText = strings[0]; 223 } else { 224 StringBuilder sb = new StringBuilder(64); 225 for (String string: strings) { 226 sb.append(string); 227 } 228 logText = sb.toString(); 229 } 230 Log.d(TAG, logText); 231 if (Eas.FILE_LOG) { 232 FileLogger.log(TAG, logText); 233 } 234 } 235 } 236 237 /** 238 * Error log is used for serious issues that should always be logged 239 * @param str the string to log 240 */ 241 public void errorLog(String str) { 242 Log.e(TAG, str); 243 if (Eas.FILE_LOG) { 244 FileLogger.log(TAG, str); 245 } 246 } 247 248 /** 249 * Waits for up to 10 seconds for network connectivity; returns whether or not there is 250 * network connectivity. 251 * 252 * @return whether there is network connectivity 253 */ 254 public boolean hasConnectivity() { 255 ConnectivityManager cm = 256 (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 257 int tries = 0; 258 while (tries++ < 1) { 259 NetworkInfo info = cm.getActiveNetworkInfo(); 260 if (info != null && info.isConnected()) { 261 DetailedState state = info.getDetailedState(); 262 if (state == DetailedState.CONNECTED) { 263 return true; 264 } 265 } 266 try { 267 Thread.sleep(10*SECONDS); 268 } catch (InterruptedException e) { 269 } 270 } 271 return false; 272 } 273 274 /** 275 * PartRequest handling (common functionality) 276 * Can be overridden if desired, but IMAP/EAS both use the next three methods as-is 277 */ 278 279 public void addPartRequest(PartRequest req) { 280 synchronized (mPartRequests) { 281 mPartRequests.add(req); 282 mRequestTime = System.currentTimeMillis(); 283 } 284 } 285 286 public void removePartRequest(PartRequest req) { 287 synchronized (mPartRequests) { 288 mPartRequests.remove(req); 289 } 290 } 291 292 public PartRequest hasPartRequest(long emailId, String part) { 293 synchronized (mPartRequests) { 294 for (PartRequest pr : mPartRequests) { 295 if (pr.emailId == emailId && pr.loc.equals(part)) 296 return pr; 297 } 298 } 299 return null; 300 } 301 302 // cancelPartRequest is sent in response to user input to stop an attachment load 303 // that is in progress. This will almost certainly require code overriding the base 304 // functionality, as sockets may need to be closed, etc. and this functionality will be 305 // service dependent. This returns the canceled PartRequest or null 306 public PartRequest cancelPartRequest(long emailId, String part) { 307 synchronized (mPartRequests) { 308 PartRequest p = null; 309 for (PartRequest pr : mPartRequests) { 310 if (pr.emailId == emailId && pr.loc.equals(part)) { 311 p = pr; 312 break; 313 } 314 } 315 if (p != null) { 316 mPartRequests.remove(p); 317 return p; 318 } 319 } 320 return null; 321 } 322} 323