Home | History | Annotate | Download | only in service
      1 /*
      2  * Copyright (C) 2009 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.content.AbstractThreadedSyncAdapter;
     20 import android.content.ContentProviderClient;
     21 import android.content.ContentResolver;
     22 import android.content.Context;
     23 import android.content.SyncResult;
     24 import android.database.Cursor;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.os.RemoteException;
     28 import android.provider.ContactsContract.Groups;
     29 import android.provider.ContactsContract.RawContacts;
     30 import android.util.Log;
     31 
     32 import com.android.emailcommon.provider.Account;
     33 import com.android.emailcommon.provider.EmailContent;
     34 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
     35 import com.android.emailcommon.provider.Mailbox;
     36 import com.android.emailcommon.service.EmailServiceStatus;
     37 import com.android.exchange.Eas;
     38 import com.android.mail.utils.LogUtils;
     39 
     40 public class ContactsSyncAdapterService extends AbstractSyncAdapterService {
     41     private static final String TAG = Eas.LOG_TAG;
     42     private static final String ACCOUNT_AND_TYPE_CONTACTS =
     43         MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CONTACTS;
     44 
     45     private static final Object sSyncAdapterLock = new Object();
     46     private static AbstractThreadedSyncAdapter sSyncAdapter = null;
     47 
     48     public ContactsSyncAdapterService() {
     49         super();
     50     }
     51 
     52     @Override
     53     protected AbstractThreadedSyncAdapter getSyncAdapter() {
     54         synchronized (sSyncAdapterLock) {
     55             if (sSyncAdapter == null) {
     56                 sSyncAdapter = new SyncAdapterImpl(this);
     57             }
     58             return sSyncAdapter;
     59         }
     60     }
     61 
     62     private class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
     63         public SyncAdapterImpl(Context context) {
     64             super(context, true /* autoInitialize */);
     65         }
     66 
     67         @Override
     68         public void onPerformSync(android.accounts.Account acct, Bundle extras,
     69                 String authority, ContentProviderClient provider, SyncResult syncResult) {
     70             if (LogUtils.isLoggable(TAG, Log.DEBUG)) {
     71                 LogUtils.d(TAG, "onPerformSync contacts starting %s, %s", acct.toString(),
     72                         extras.toString());
     73             } else {
     74                 LogUtils.i(TAG, "onPerformSync contacts starting %s", extras.toString());
     75             }
     76             if (!waitForService()) {
     77                 // The service didn't connect, nothing we can do.
     78                 return;
     79             }
     80 
     81             final Account emailAccount = Account.restoreAccountWithAddress(
     82                     ContactsSyncAdapterService.this, acct.name);
     83             if (emailAccount == null) {
     84                 // There could be a timing issue with onPerformSync() being called and
     85                 // the account being removed from our database.
     86                 LogUtils.w(TAG,
     87                         "onPerformSync() - Could not find an Account, skipping contacts sync.");
     88                 return;
     89             }
     90 
     91             // TODO: is this still needed?
     92             // If we've been asked to do an upload, make sure we've got work to do
     93             if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
     94                 Uri uri = RawContacts.CONTENT_URI.buildUpon()
     95                         .appendQueryParameter(RawContacts.ACCOUNT_NAME, acct.name)
     96                         .appendQueryParameter(RawContacts.ACCOUNT_TYPE,
     97                                 Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE)
     98                         .build();
     99                 // See if we've got dirty contacts or dirty groups containing our contacts
    100                 boolean changed = hasDirtyRows(getContentResolver(), uri, RawContacts.DIRTY);
    101                 if (!changed) {
    102                     uri = Groups.CONTENT_URI.buildUpon()
    103                             .appendQueryParameter(RawContacts.ACCOUNT_NAME, acct.name)
    104                             .appendQueryParameter(RawContacts.ACCOUNT_TYPE,
    105                                     Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE)
    106                             .build();
    107                     changed = hasDirtyRows(getContentResolver(), uri, Groups.DIRTY);
    108                 }
    109                 if (!changed) {
    110                     LogUtils.d(TAG, "Upload sync; no changes");
    111                     return;
    112                 }
    113             }
    114 
    115             // TODO: move this to some common place.
    116             // Push only means this sync request should only refresh the ping (either because
    117             // settings changed, or we need to restart it for some reason).
    118             final boolean pushOnly = Mailbox.isPushOnlyExtras(extras);
    119 
    120             if (pushOnly) {
    121                 LogUtils.d(TAG, "onPerformSync email: mailbox push only");
    122                 if (mEasService != null) {
    123                     try {
    124                         mEasService.pushModify(emailAccount.mId);
    125                         return;
    126                     } catch (final RemoteException re) {
    127                         LogUtils.e(TAG, re, "While trying to pushModify within onPerformSync");
    128                         // TODO: how to handle this?
    129                     }
    130                 }
    131                 return;
    132             } else {
    133                 try {
    134                     final int result = mEasService.sync(emailAccount.mId, extras);
    135                     writeResultToSyncResult(result, syncResult);
    136                     if (syncResult.stats.numAuthExceptions > 0 &&
    137                             result != EmailServiceStatus.PROVISIONING_ERROR) {
    138                         showAuthNotification(emailAccount.mId, emailAccount.mEmailAddress);
    139                     }
    140                 } catch (RemoteException e) {
    141                     LogUtils.e(TAG, e, "While trying to pushModify within onPerformSync");
    142                 }
    143             }
    144 
    145             LogUtils.d(TAG, "onPerformSync contacts: finished");
    146         }
    147     }
    148 
    149     private static boolean hasDirtyRows(ContentResolver resolver, Uri uri, String dirtyColumn) {
    150         Cursor c = resolver.query(uri, EmailContent.ID_PROJECTION, dirtyColumn + "=1", null, null);
    151         if (c == null) {
    152             return false;
    153         }
    154         try {
    155             return c.getCount() > 0;
    156         } finally {
    157             c.close();
    158         }
    159     }
    160 }
    161