| /* |
| * Copyright (C) 2008-2009 Marc Blank |
| * Licensed to The Android Open Source Project. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.exchange; |
| |
| import com.android.emailcommon.provider.Account; |
| import com.android.emailcommon.provider.HostAuth; |
| import com.android.emailcommon.provider.Mailbox; |
| import com.android.exchange.utility.FileLogger; |
| |
| import android.content.Context; |
| import android.net.ConnectivityManager; |
| import android.net.NetworkInfo; |
| import android.net.NetworkInfo.DetailedState; |
| import android.os.Bundle; |
| import android.util.Log; |
| |
| import java.util.concurrent.LinkedBlockingQueue; |
| |
| /** |
| * Base class for all protocol services SyncManager (extends Service, implements |
| * Runnable) instantiates subclasses to run a sync (either timed, or push, or |
| * mail placed in outbox, etc.) EasSyncService is currently implemented; my goal |
| * would be to move IMAP to this structure when it comes time to introduce push |
| * functionality. |
| */ |
| public abstract class AbstractSyncService implements Runnable { |
| |
| public String TAG = "AbstractSyncService"; |
| |
| public static final int SECONDS = 1000; |
| public static final int MINUTES = 60*SECONDS; |
| public static final int HOURS = 60*MINUTES; |
| public static final int DAYS = 24*HOURS; |
| |
| public static final int CONNECT_TIMEOUT = 30*SECONDS; |
| public static final int NETWORK_WAIT = 15*SECONDS; |
| |
| public static final String EAS_PROTOCOL = "eas"; |
| public static final int EXIT_DONE = 0; |
| public static final int EXIT_IO_ERROR = 1; |
| public static final int EXIT_LOGIN_FAILURE = 2; |
| public static final int EXIT_EXCEPTION = 3; |
| public static final int EXIT_SECURITY_FAILURE = 4; |
| public static final int EXIT_ACCESS_DENIED = 5; |
| |
| public Mailbox mMailbox; |
| protected long mMailboxId; |
| protected int mExitStatus = EXIT_EXCEPTION; |
| protected String mMailboxName; |
| public Account mAccount; |
| public Context mContext; |
| public int mChangeCount = 0; |
| public volatile int mSyncReason = 0; |
| protected volatile boolean mStop = false; |
| protected volatile Thread mThread; |
| protected final Object mSynchronizer = new Object(); |
| |
| protected volatile long mRequestTime = 0; |
| protected LinkedBlockingQueue<Request> mRequestQueue = new LinkedBlockingQueue<Request>(); |
| |
| /** |
| * Sent by SyncManager to request that the service stop itself cleanly |
| */ |
| public abstract void stop(); |
| |
| /** |
| * Sent by SyncManager to indicate that an alarm has fired for this service, and that its |
| * pending (network) operation has timed out. The service is NOT automatically stopped, |
| * although the behavior is service dependent. |
| * |
| * @return true if the operation was stopped normally; false if the thread needed to be |
| * interrupted. |
| */ |
| public abstract boolean alarm(); |
| |
| /** |
| * Sent by SyncManager to request that the service reset itself cleanly; the meaning of this |
| * operation is service dependent. |
| */ |
| public abstract void reset(); |
| |
| /** |
| * Called to validate an account; abstract to allow each protocol to do what |
| * is necessary. For consistency with the Email app's original |
| * functionality, success is indicated by a failure to throw an Exception |
| * (ugh). Parameters are self-explanatory |
| * |
| * @param hostAuth |
| * @return a Bundle containing a result code and, depending on the result, a PolicySet or an |
| * error message |
| */ |
| public abstract Bundle validateAccount(HostAuth hostAuth, Context context); |
| |
| public AbstractSyncService(Context _context, Mailbox _mailbox) { |
| mContext = _context; |
| mMailbox = _mailbox; |
| mMailboxId = _mailbox.mId; |
| mMailboxName = _mailbox.mServerId; |
| mAccount = Account.restoreAccountWithId(_context, _mailbox.mAccountKey); |
| } |
| |
| // Will be required when subclasses are instantiated by name |
| public AbstractSyncService(String prefix) { |
| } |
| |
| /** |
| * The UI can call this static method to perform account validation. This method wraps each |
| * protocol's validateAccount method. Arguments are self-explanatory, except where noted. |
| * |
| * @param klass the protocol class (EasSyncService.class for example) |
| * @param hostAuth |
| * @param context |
| * @return a Bundle containing a result code and, depending on the result, a PolicySet or an |
| * error message |
| */ |
| public static Bundle validate(Class<? extends AbstractSyncService> klass, |
| HostAuth hostAuth, Context context) { |
| AbstractSyncService svc; |
| try { |
| svc = klass.newInstance(); |
| return svc.validateAccount(hostAuth, context); |
| } catch (IllegalAccessException e) { |
| } catch (InstantiationException e) { |
| } |
| return null; |
| } |
| |
| public static class ValidationResult { |
| static final int NO_FAILURE = 0; |
| static final int CONNECTION_FAILURE = 1; |
| static final int VALIDATION_FAILURE = 2; |
| static final int EXCEPTION = 3; |
| |
| static final ValidationResult succeeded = new ValidationResult(true, NO_FAILURE, null); |
| boolean success; |
| int failure = NO_FAILURE; |
| String reason = null; |
| Exception exception = null; |
| |
| ValidationResult(boolean _success, int _failure, String _reason) { |
| success = _success; |
| failure = _failure; |
| reason = _reason; |
| } |
| |
| ValidationResult(boolean _success) { |
| success = _success; |
| } |
| |
| ValidationResult(Exception e) { |
| success = false; |
| failure = EXCEPTION; |
| exception = e; |
| } |
| |
| public boolean isSuccess() { |
| return success; |
| } |
| |
| public String getReason() { |
| return reason; |
| } |
| } |
| |
| public boolean isStopped() { |
| return mStop; |
| } |
| |
| public Object getSynchronizer() { |
| return mSynchronizer; |
| } |
| |
| /** |
| * Convenience methods to do user logging (i.e. connection activity). Saves a bunch of |
| * repetitive code. |
| */ |
| public void userLog(String string, int code, String string2) { |
| if (Eas.USER_LOG) { |
| userLog(string + code + string2); |
| } |
| } |
| |
| public void userLog(String string, int code) { |
| if (Eas.USER_LOG) { |
| userLog(string + code); |
| } |
| } |
| |
| public void userLog(String str, Exception e) { |
| if (Eas.USER_LOG) { |
| Log.e(TAG, str, e); |
| } else { |
| Log.e(TAG, str + e); |
| } |
| if (Eas.FILE_LOG) { |
| FileLogger.log(e); |
| } |
| } |
| |
| /** |
| * Standard logging for EAS. |
| * If user logging is active, we concatenate any arguments and log them using Log.d |
| * We also check for file logging, and log appropriately |
| * @param strings strings to concatenate and log |
| */ |
| public void userLog(String ...strings) { |
| if (Eas.USER_LOG) { |
| String logText; |
| if (strings.length == 1) { |
| logText = strings[0]; |
| } else { |
| StringBuilder sb = new StringBuilder(64); |
| for (String string: strings) { |
| sb.append(string); |
| } |
| logText = sb.toString(); |
| } |
| Log.d(TAG, logText); |
| if (Eas.FILE_LOG) { |
| FileLogger.log(TAG, logText); |
| } |
| } |
| } |
| |
| /** |
| * Error log is used for serious issues that should always be logged |
| * @param str the string to log |
| */ |
| public void errorLog(String str) { |
| Log.e(TAG, str); |
| if (Eas.FILE_LOG) { |
| FileLogger.log(TAG, str); |
| } |
| } |
| |
| /** |
| * Waits for up to 10 seconds for network connectivity; returns whether or not there is |
| * network connectivity. |
| * |
| * @return whether there is network connectivity |
| */ |
| public boolean hasConnectivity() { |
| ConnectivityManager cm = |
| (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); |
| int tries = 0; |
| while (tries++ < 1) { |
| // Use the same test as in ExchangeService#waitForConnectivity |
| // TODO: Create common code for this test in emailcommon |
| NetworkInfo info = cm.getActiveNetworkInfo(); |
| if (info != null) { |
| return true; |
| } |
| try { |
| Thread.sleep(10*SECONDS); |
| } catch (InterruptedException e) { |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Request handling (common functionality) |
| * Can be overridden if desired |
| */ |
| |
| public void addRequest(Request req) { |
| mRequestQueue.offer(req); |
| } |
| |
| public void removeRequest(Request req) { |
| mRequestQueue.remove(req); |
| } |
| |
| public boolean hasPendingRequests() { |
| return !mRequestQueue.isEmpty(); |
| } |
| |
| public void clearRequests() { |
| mRequestQueue.clear(); |
| } |
| } |