Home | History | Annotate | Download | only in service
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.exchange.service;
     18 
     19 import android.app.Notification;
     20 import android.app.NotificationManager;
     21 import android.app.PendingIntent;
     22 import android.app.Service;
     23 import android.content.AbstractThreadedSyncAdapter;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.ServiceConnection;
     28 import android.content.SyncResult;
     29 import android.database.Cursor;
     30 import android.net.Uri;
     31 import android.os.IBinder;
     32 import android.text.format.DateUtils;
     33 
     34 import com.android.emailcommon.provider.Account;
     35 import com.android.emailcommon.provider.EmailContent;
     36 import com.android.emailcommon.service.EmailServiceStatus;
     37 import com.android.emailcommon.service.IEmailService;
     38 import com.android.emailcommon.utility.IntentUtilities;
     39 import com.android.exchange.R;
     40 import com.android.mail.utils.LogUtils;
     41 
     42 /**
     43  * Base class for services that handle sync requests from the system SyncManager.
     44  * This class covers the boilerplate for using an {@link AbstractThreadedSyncAdapter}. Subclasses
     45  * should just implement their sync adapter, and override {@link #getSyncAdapter}.
     46  */
     47 public abstract class AbstractSyncAdapterService extends Service {
     48     private static final String TAG = LogUtils.TAG;
     49 
     50     // The call to ServiceConnection.onServiceConnected is asynchronous to bindService. It's
     51     // possible for that to be delayed if, in which case, a call to onPerformSync
     52     // could occur before we have a connection to the service.
     53     // In onPerformSync, if we don't yet have our EasService, we will wait for up to 10
     54     // seconds for it to appear. If it takes longer than that, we will fail the sync.
     55     private static final long MAX_WAIT_FOR_SERVICE_MS = 10 * DateUtils.SECOND_IN_MILLIS;
     56 
     57     public AbstractSyncAdapterService() {
     58         super();
     59     }
     60 
     61     protected IEmailService mEasService;
     62     protected ServiceConnection mConnection;
     63 
     64     @Override
     65     public void onCreate() {
     66         super.onCreate();
     67         // Make sure EmailContent is initialized in Exchange app
     68         EmailContent.init(this);
     69         mConnection = new ServiceConnection() {
     70             @Override
     71             public void onServiceConnected(ComponentName name,  IBinder binder) {
     72                 LogUtils.v(TAG, "onServiceConnected");
     73                 synchronized (mConnection) {
     74                     mEasService = IEmailService.Stub.asInterface(binder);
     75                     mConnection.notify();
     76                 }
     77             }
     78 
     79             @Override
     80             public void onServiceDisconnected(ComponentName name) {
     81                 mEasService = null;
     82             }
     83         };
     84         bindService(new Intent(this, EasService.class), mConnection, Context.BIND_AUTO_CREATE);
     85     }
     86 
     87     @Override
     88     public void onDestroy() {
     89         super.onDestroy();
     90         unbindService(mConnection);
     91     }
     92 
     93     @Override
     94     public IBinder onBind(Intent intent) {
     95         return getSyncAdapter().getSyncAdapterBinder();
     96     }
     97 
     98     /**
     99      * Subclasses should override this to supply an instance of its sync adapter. Best practice is
    100      * to create a singleton and return that.
    101      * @return An instance of the sync adapter.
    102      */
    103     protected abstract AbstractThreadedSyncAdapter getSyncAdapter();
    104 
    105     /**
    106      * Create and return an intent to display (and edit) settings for a specific account, or -1
    107      * for any/all accounts.  If an account name string is provided, a warning dialog will be
    108      * displayed as well.
    109      */
    110     public static Intent createAccountSettingsIntent(long accountId, String accountName) {
    111         final Uri.Builder builder = IntentUtilities.createActivityIntentUrlBuilder(
    112                 IntentUtilities.PATH_SETTINGS);
    113         IntentUtilities.setAccountId(builder, accountId);
    114         IntentUtilities.setAccountName(builder, accountName);
    115         return new Intent(Intent.ACTION_EDIT, builder.build());
    116     }
    117 
    118     protected void showAuthNotification(long accountId, String accountName) {
    119         final PendingIntent pendingIntent = PendingIntent.getActivity(
    120                 this,
    121                 0,
    122                 createAccountSettingsIntent(accountId, accountName),
    123                 0);
    124 
    125         final Notification notification = new Notification.Builder(this)
    126                 .setContentTitle(this.getString(R.string.auth_error_notification_title))
    127                 .setContentText(this.getString(
    128                         R.string.auth_error_notification_text, accountName))
    129                 .setSmallIcon(R.drawable.stat_notify_auth)
    130                 .setContentIntent(pendingIntent)
    131                 .setAutoCancel(true)
    132                 .getNotification();
    133 
    134         final NotificationManager nm = (NotificationManager)
    135                 this.getSystemService(Context.NOTIFICATION_SERVICE);
    136         nm.notify("AuthError", 0, notification);
    137     }
    138 
    139     /**
    140      * Interpret a result code from an {@link IEmailService.sync()} and, if it's an error, write
    141      * it to the appropriate field in {@link android.content.SyncResult}.
    142      * @param result
    143      * @param syncResult
    144      * @return Whether an error code was written to syncResult.
    145      */
    146     public static boolean writeResultToSyncResult(final int result, final SyncResult syncResult) {
    147         switch (result) {
    148             case EmailServiceStatus.SUCCESS:
    149                 return false;
    150 
    151             case EmailServiceStatus.REMOTE_EXCEPTION:
    152             case EmailServiceStatus.LOGIN_FAILED:
    153             case EmailServiceStatus.SECURITY_FAILURE:
    154             case EmailServiceStatus.CLIENT_CERTIFICATE_ERROR:
    155             case EmailServiceStatus.ACCESS_DENIED:
    156                 syncResult.stats.numAuthExceptions = 1;
    157                 return true;
    158 
    159             case EmailServiceStatus.HARD_DATA_ERROR:
    160             case EmailServiceStatus.INTERNAL_ERROR:
    161                 syncResult.databaseError = true;
    162                 return true;
    163 
    164             case EmailServiceStatus.CONNECTION_ERROR:
    165             case EmailServiceStatus.IO_ERROR:
    166                 syncResult.stats.numIoExceptions = 1;
    167                 return true;
    168 
    169             case EmailServiceStatus.TOO_MANY_REDIRECTS:
    170                 syncResult.tooManyRetries = true;
    171                 return true;
    172 
    173             case EmailServiceStatus.IN_PROGRESS:
    174             case EmailServiceStatus.MESSAGE_NOT_FOUND:
    175             case EmailServiceStatus.ATTACHMENT_NOT_FOUND:
    176             case EmailServiceStatus.FOLDER_NOT_DELETED:
    177             case EmailServiceStatus.FOLDER_NOT_RENAMED:
    178             case EmailServiceStatus.FOLDER_NOT_CREATED:
    179             case EmailServiceStatus.ACCOUNT_UNINITIALIZED:
    180             case EmailServiceStatus.PROTOCOL_ERROR:
    181                 LogUtils.e(TAG, "Unexpected sync result %d", result);
    182                 return false;
    183         }
    184         return false;
    185     }
    186 
    187     protected final boolean waitForService() {
    188         synchronized(mConnection) {
    189             if (mEasService == null) {
    190                 LogUtils.d(TAG, "service not yet connected");
    191                 try {
    192                     mConnection.wait(MAX_WAIT_FOR_SERVICE_MS);
    193                 } catch (InterruptedException e) {
    194                     LogUtils.wtf(TAG, "InterrupedException waiting for EasService to connect");
    195                     return false;
    196                 }
    197                 if (mEasService == null) {
    198                     LogUtils.wtf(TAG, "timed out waiting for EasService to connect");
    199                     return false;
    200                 }
    201             }
    202         }
    203         return true;
    204     }
    205 
    206     protected final Account getAccountFromAndroidAccount(final android.accounts.Account acct) {
    207         final Account emailAccount;
    208         emailAccount = Account.restoreAccountWithAddress(this, acct.name);
    209         return emailAccount;
    210     }
    211 
    212 }
    213 
    214