Home | History | Annotate | Download | only in emailsync
      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