Home | History | Annotate | Download | only in exchange
      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;
     18 
     19 import com.android.emailcommon.provider.EmailContent.AccountColumns;
     20 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
     21 import com.android.emailcommon.provider.Mailbox;
     22 
     23 import android.accounts.Account;
     24 import android.accounts.OperationCanceledException;
     25 import android.app.Service;
     26 import android.content.AbstractThreadedSyncAdapter;
     27 import android.content.ContentProviderClient;
     28 import android.content.ContentResolver;
     29 import android.content.Context;
     30 import android.content.Intent;
     31 import android.content.SyncResult;
     32 import android.database.Cursor;
     33 import android.net.Uri;
     34 import android.os.Bundle;
     35 import android.os.IBinder;
     36 import android.provider.ContactsContract.Groups;
     37 import android.provider.ContactsContract.RawContacts;
     38 import android.util.Log;
     39 
     40 public class ContactsSyncAdapterService extends Service {
     41     private static final String TAG = "EAS ContactsSyncAdapterService";
     42     private static SyncAdapterImpl sSyncAdapter = null;
     43     private static final Object sSyncAdapterLock = new Object();
     44 
     45     private static final String[] ID_PROJECTION = new String[] {"_id"};
     46     private static final String ACCOUNT_AND_TYPE_CONTACTS =
     47         MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CONTACTS;
     48 
     49     public ContactsSyncAdapterService() {
     50         super();
     51     }
     52 
     53     private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
     54         private Context mContext;
     55 
     56         public SyncAdapterImpl(Context context) {
     57             super(context, true /* autoInitialize */);
     58             mContext = context;
     59         }
     60 
     61         @Override
     62         public void onPerformSync(Account account, Bundle extras,
     63                 String authority, ContentProviderClient provider, SyncResult syncResult) {
     64             try {
     65                 ContactsSyncAdapterService.performSync(mContext, account, extras,
     66                         authority, provider, syncResult);
     67             } catch (OperationCanceledException e) {
     68             }
     69         }
     70     }
     71 
     72     @Override
     73     public void onCreate() {
     74         super.onCreate();
     75         synchronized (sSyncAdapterLock) {
     76             if (sSyncAdapter == null) {
     77                 sSyncAdapter = new SyncAdapterImpl(getApplicationContext());
     78             }
     79         }
     80     }
     81 
     82     @Override
     83     public IBinder onBind(Intent intent) {
     84         return sSyncAdapter.getSyncAdapterBinder();
     85     }
     86 
     87     private static boolean hasDirtyRows(ContentResolver resolver, Uri uri, String dirtyColumn) {
     88         Cursor c = resolver.query(uri, ID_PROJECTION, dirtyColumn + "=1", null, null);
     89         try {
     90             return c.getCount() > 0;
     91         } finally {
     92             c.close();
     93         }
     94     }
     95 
     96     /**
     97      * Partial integration with system SyncManager; we tell our EAS ExchangeService to start a
     98      * contacts sync when we get the signal from SyncManager.
     99      * The missing piece at this point is integration with the push/ping mechanism in EAS; this will
    100      * be put in place at a later time.
    101      */
    102     private static void performSync(Context context, Account account, Bundle extras,
    103             String authority, ContentProviderClient provider, SyncResult syncResult)
    104             throws OperationCanceledException {
    105         ContentResolver cr = context.getContentResolver();
    106 
    107         // If we've been asked to do an upload, make sure we've got work to do
    108         if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
    109             Uri uri = RawContacts.CONTENT_URI.buildUpon()
    110                 .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
    111                 .appendQueryParameter(RawContacts.ACCOUNT_TYPE, Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE)
    112                 .build();
    113             // See if we've got dirty contacts or dirty groups containing our contacts
    114             boolean changed = hasDirtyRows(cr, uri, RawContacts.DIRTY);
    115             if (!changed) {
    116                 uri = Groups.CONTENT_URI.buildUpon()
    117                     .appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
    118                     .appendQueryParameter(RawContacts.ACCOUNT_TYPE,
    119                             Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE)
    120                     .build();
    121                 changed = hasDirtyRows(cr, uri, Groups.DIRTY);
    122             }
    123             if (!changed) {
    124                 Log.i(TAG, "Upload sync; no changes");
    125                 return;
    126             }
    127         }
    128 
    129         // Find the (EmailProvider) account associated with this email address
    130         Cursor accountCursor =
    131             cr.query(com.android.emailcommon.provider.Account.CONTENT_URI, ID_PROJECTION,
    132                 AccountColumns.EMAIL_ADDRESS + "=?", new String[] {account.name}, null);
    133         try {
    134             if (accountCursor.moveToFirst()) {
    135                 long accountId = accountCursor.getLong(0);
    136                 // Now, find the contacts mailbox associated with the account
    137                 Cursor mailboxCursor = cr.query(Mailbox.CONTENT_URI, ID_PROJECTION,
    138                         ACCOUNT_AND_TYPE_CONTACTS, new String[] {Long.toString(accountId)}, null);
    139                 try {
    140                      if (mailboxCursor.moveToFirst()) {
    141                         Log.i(TAG, "Contact sync requested for " + account.name);
    142                         // Ask for a sync from our sync manager
    143                         ExchangeService.serviceRequest(mailboxCursor.getLong(0),
    144                                 ExchangeService.SYNC_UPSYNC);
    145                     }
    146                 } finally {
    147                     mailboxCursor.close();
    148                 }
    149             }
    150         } finally {
    151             accountCursor.close();
    152         }
    153     }
    154 }