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