1147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank/* 2147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * Copyright (C) 2009 The Android Open Source Project 3147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * 4147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 5147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * you may not use this file except in compliance with the License. 6147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * You may obtain a copy of the License at 7147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * 8147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * http://www.apache.org/licenses/LICENSE-2.0 9147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * 10147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * Unless required by applicable law or agreed to in writing, software 11147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * distributed under the License is distributed on an "AS IS" BASIS, 12147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * See the License for the specific language governing permissions and 14147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank * limitations under the License. 15147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank */ 16147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 17bbafcf7dbcb92063aa207113451c1d29235bc5ddMarc Blankpackage com.android.exchange.service; 18147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 19147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blankimport android.content.AbstractThreadedSyncAdapter; 20147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blankimport android.content.ContentProviderClient; 21147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blankimport android.content.ContentResolver; 224fbb88b5f46f4890106fa6302f87511a72042129Marc Blankimport android.content.Context; 23147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blankimport android.content.SyncResult; 24147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blankimport android.database.Cursor; 255d5c395c2d6ff0574a73802e3adab70e907e1b53Marc Blankimport android.net.Uri; 26147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blankimport android.os.Bundle; 27f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdonimport android.os.RemoteException; 28c6f2846fe5e708abd194924df294d223b3d6a723Marc Blankimport android.provider.ContactsContract.Groups; 295d5c395c2d6ff0574a73802e3adab70e907e1b53Marc Blankimport android.provider.ContactsContract.RawContacts; 303eef378426c7c88608f53f5a268baed40259ccf6Alon Albertimport android.util.Log; 31147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 32f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdonimport com.android.emailcommon.provider.Account; 33b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Huimport com.android.emailcommon.provider.EmailContent; 34b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Huimport com.android.emailcommon.provider.EmailContent.MailboxColumns; 35b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Huimport com.android.emailcommon.provider.Mailbox; 36f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdonimport com.android.emailcommon.service.EmailServiceStatus; 37b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Huimport com.android.exchange.Eas; 38b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Huimport com.android.mail.utils.LogUtils; 39b31070f7484eeeb15c4bce89dbc61388d05d0bfcYu Ping Hu 40bbafcf7dbcb92063aa207113451c1d29235bc5ddMarc Blankpublic class ContactsSyncAdapterService extends AbstractSyncAdapterService { 41110837ebff288a75f9bda067c38e2c46797d99b5Alon Albert private static final String TAG = Eas.LOG_TAG; 42147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank private static final String ACCOUNT_AND_TYPE_CONTACTS = 43147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CONTACTS; 44147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 4524e1187f8511d301fa586759cd1b3bd5ad2ccf41Yu Ping Hu private static final Object sSyncAdapterLock = new Object(); 4624e1187f8511d301fa586759cd1b3bd5ad2ccf41Yu Ping Hu private static AbstractThreadedSyncAdapter sSyncAdapter = null; 4724e1187f8511d301fa586759cd1b3bd5ad2ccf41Yu Ping Hu 48147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank public ContactsSyncAdapterService() { 49147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank super(); 50147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank } 51147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 529696d7f701599644d1c108863d6195af88da2c30Yu Ping Hu @Override 5324e1187f8511d301fa586759cd1b3bd5ad2ccf41Yu Ping Hu protected AbstractThreadedSyncAdapter getSyncAdapter() { 5424e1187f8511d301fa586759cd1b3bd5ad2ccf41Yu Ping Hu synchronized (sSyncAdapterLock) { 5524e1187f8511d301fa586759cd1b3bd5ad2ccf41Yu Ping Hu if (sSyncAdapter == null) { 5624e1187f8511d301fa586759cd1b3bd5ad2ccf41Yu Ping Hu sSyncAdapter = new SyncAdapterImpl(this); 5724e1187f8511d301fa586759cd1b3bd5ad2ccf41Yu Ping Hu } 5824e1187f8511d301fa586759cd1b3bd5ad2ccf41Yu Ping Hu return sSyncAdapter; 5924e1187f8511d301fa586759cd1b3bd5ad2ccf41Yu Ping Hu } 609696d7f701599644d1c108863d6195af88da2c30Yu Ping Hu } 619696d7f701599644d1c108863d6195af88da2c30Yu Ping Hu 62f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon private class SyncAdapterImpl extends AbstractThreadedSyncAdapter { 63fba54ab0b9b4cd3d24624c2c988e2ee422f8a59eFred Quintana public SyncAdapterImpl(Context context) { 64fba54ab0b9b4cd3d24624c2c988e2ee422f8a59eFred Quintana super(context, true /* autoInitialize */); 65147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank } 66147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 67147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank @Override 68f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon public void onPerformSync(android.accounts.Account acct, Bundle extras, 69147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank String authority, ContentProviderClient provider, SyncResult syncResult) { 703eef378426c7c88608f53f5a268baed40259ccf6Alon Albert if (LogUtils.isLoggable(TAG, Log.DEBUG)) { 71f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon LogUtils.d(TAG, "onPerformSync contacts starting %s, %s", acct.toString(), 723eef378426c7c88608f53f5a268baed40259ccf6Alon Albert extras.toString()); 733eef378426c7c88608f53f5a268baed40259ccf6Alon Albert } else { 74f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon LogUtils.i(TAG, "onPerformSync contacts starting %s", extras.toString()); 753eef378426c7c88608f53f5a268baed40259ccf6Alon Albert } 76f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon if (!waitForService()) { 77f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon // The service didn't connect, nothing we can do. 78f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon return; 79f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } 80f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon 81f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon final Account emailAccount = Account.restoreAccountWithAddress( 82f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon ContactsSyncAdapterService.this, acct.name); 83db1cd8456a0cf273890cd3da10d0d5f00aadbb45Anthony Lee if (emailAccount == null) { 84db1cd8456a0cf273890cd3da10d0d5f00aadbb45Anthony Lee // There could be a timing issue with onPerformSync() being called and 85db1cd8456a0cf273890cd3da10d0d5f00aadbb45Anthony Lee // the account being removed from our database. 86db1cd8456a0cf273890cd3da10d0d5f00aadbb45Anthony Lee LogUtils.w(TAG, 87db1cd8456a0cf273890cd3da10d0d5f00aadbb45Anthony Lee "onPerformSync() - Could not find an Account, skipping contacts sync."); 88db1cd8456a0cf273890cd3da10d0d5f00aadbb45Anthony Lee return; 89db1cd8456a0cf273890cd3da10d0d5f00aadbb45Anthony Lee } 90f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon 91f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon // TODO: is this still needed? 92f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon // If we've been asked to do an upload, make sure we've got work to do 93f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) { 94f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon Uri uri = RawContacts.CONTENT_URI.buildUpon() 95f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon .appendQueryParameter(RawContacts.ACCOUNT_NAME, acct.name) 96f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon .appendQueryParameter(RawContacts.ACCOUNT_TYPE, 97f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE) 98f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon .build(); 99f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon // See if we've got dirty contacts or dirty groups containing our contacts 100f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon boolean changed = hasDirtyRows(getContentResolver(), uri, RawContacts.DIRTY); 101f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon if (!changed) { 102f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon uri = Groups.CONTENT_URI.buildUpon() 103f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon .appendQueryParameter(RawContacts.ACCOUNT_NAME, acct.name) 104f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon .appendQueryParameter(RawContacts.ACCOUNT_TYPE, 105f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon Eas.EXCHANGE_ACCOUNT_MANAGER_TYPE) 106f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon .build(); 107f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon changed = hasDirtyRows(getContentResolver(), uri, Groups.DIRTY); 108f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } 109f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon if (!changed) { 110f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon LogUtils.d(TAG, "Upload sync; no changes"); 111f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon return; 112f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } 113f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } 114f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon 115f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon // TODO: move this to some common place. 116f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon // Push only means this sync request should only refresh the ping (either because 117f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon // settings changed, or we need to restart it for some reason). 118f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon final boolean pushOnly = Mailbox.isPushOnlyExtras(extras); 119f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon 120f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon if (pushOnly) { 121f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon LogUtils.d(TAG, "onPerformSync email: mailbox push only"); 122f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon if (mEasService != null) { 123f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon try { 124f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon mEasService.pushModify(emailAccount.mId); 125f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon return; 126f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } catch (final RemoteException re) { 127f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon LogUtils.e(TAG, re, "While trying to pushModify within onPerformSync"); 128f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon // TODO: how to handle this? 129f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } 130f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } 131f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon return; 132f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } else { 133f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon try { 134f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon final int result = mEasService.sync(emailAccount.mId, extras); 135f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon writeResultToSyncResult(result, syncResult); 136f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon if (syncResult.stats.numAuthExceptions > 0 && 137f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon result != EmailServiceStatus.PROVISIONING_ERROR) { 138f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon showAuthNotification(emailAccount.mId, emailAccount.mEmailAddress); 139f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } 140f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } catch (RemoteException e) { 141f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon LogUtils.e(TAG, e, "While trying to pushModify within onPerformSync"); 142f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } 143f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon } 144f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon 145f8cccaecc8148d12d58ffcba5ce7366191316ac0Martin Hibdon LogUtils.d(TAG, "onPerformSync contacts: finished"); 146147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank } 147147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank } 148147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 149c6f2846fe5e708abd194924df294d223b3d6a723Marc Blank private static boolean hasDirtyRows(ContentResolver resolver, Uri uri, String dirtyColumn) { 1506e66ab513197793c34f5dcda159043da39224ff9Yu Ping Hu Cursor c = resolver.query(uri, EmailContent.ID_PROJECTION, dirtyColumn + "=1", null, null); 151680fcf0006c3894e21952902c568d13398a6540eJay Shrauner if (c == null) { 152680fcf0006c3894e21952902c568d13398a6540eJay Shrauner return false; 153680fcf0006c3894e21952902c568d13398a6540eJay Shrauner } 154c6f2846fe5e708abd194924df294d223b3d6a723Marc Blank try { 155c6f2846fe5e708abd194924df294d223b3d6a723Marc Blank return c.getCount() > 0; 156c6f2846fe5e708abd194924df294d223b3d6a723Marc Blank } finally { 157c6f2846fe5e708abd194924df294d223b3d6a723Marc Blank c.close(); 158c6f2846fe5e708abd194924df294d223b3d6a723Marc Blank } 159c6f2846fe5e708abd194924df294d223b3d6a723Marc Blank } 1606e66ab513197793c34f5dcda159043da39224ff9Yu Ping Hu} 161