• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ContactsSyncAdapterService()48     public ContactsSyncAdapterService() {
49         super();
50     }
51 
52     @Override
getSyncAdapter()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 {
SyncAdapterImpl(Context context)63         public SyncAdapterImpl(Context context) {
64             super(context, true /* autoInitialize */);
65         }
66 
67         @Override
onPerformSync(android.accounts.Account acct, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult)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 
hasDirtyRows(ContentResolver resolver, Uri uri, String dirtyColumn)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