diff options
Diffstat (limited to 'core/java/android/content/SyncManager.java')
-rw-r--r-- | core/java/android/content/SyncManager.java | 418 |
1 files changed, 259 insertions, 159 deletions
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 4d2cce8..03cfbea 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -21,8 +21,9 @@ import com.google.android.collect.Maps; import com.android.internal.R; import com.android.internal.util.ArrayUtils; -import android.accounts.AccountMonitor; -import android.accounts.AccountMonitorListener; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.OnAccountsUpdatedListener; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; @@ -30,8 +31,10 @@ import android.app.PendingIntent; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; +import android.content.pm.RegisteredServicesCache; +import android.database.Cursor; +import android.database.DatabaseUtils; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; @@ -47,7 +50,9 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.provider.Sync; import android.provider.Settings; +import android.provider.Sync.History; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.format.Time; @@ -72,11 +77,14 @@ import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Random; +import java.util.Observer; +import java.util.Observable; +import java.util.Set; /** * @hide */ -class SyncManager { +class SyncManager implements OnAccountsUpdatedListener { private static final String TAG = "SyncManager"; // used during dumping of the Sync history @@ -117,14 +125,11 @@ class SyncManager { private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock"; private Context mContext; - private ContentResolver mContentResolver; private String mStatusText = ""; private long mHeartbeatTime = 0; - private AccountMonitor mAccountMonitor; - - private volatile String[] mAccounts = null; + private volatile Account[] mAccounts = null; volatile private PowerManager.WakeLock mSyncWakeLock; volatile private PowerManager.WakeLock mHandleAlarmWakeLock; @@ -151,10 +156,11 @@ class SyncManager { private final PendingIntent mSyncAlarmIntent; private final PendingIntent mSyncPollAlarmIntent; + private final SyncAdaptersCache mSyncAdapters; + private BroadcastReceiver mStorageIntentReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { - ensureContentResolver(); String action = intent.getAction(); if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -172,6 +178,43 @@ class SyncManager { } }; + private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + if (!mFactoryTest) { + AccountManager.get(mContext).addOnAccountsUpdatedListener(SyncManager.this, + mSyncHandler, true /* updateImmediately */); + } + } + }; + + public void onAccountsUpdated(Account[] accounts) { + final boolean hadAccountsAlready = mAccounts != null; + mAccounts = accounts; + + // if a sync is in progress yet it is no longer in the accounts list, + // cancel it + ActiveSyncContext activeSyncContext = mActiveSyncContext; + if (activeSyncContext != null) { + if (!ArrayUtils.contains(accounts, activeSyncContext.mSyncOperation.account)) { + Log.d(TAG, "canceling sync since the account has been removed"); + sendSyncFinishedOrCanceledMessage(activeSyncContext, + null /* no result since this is a cancel */); + } + } + + // we must do this since we don't bother scheduling alarms when + // the accounts are not set yet + sendCheckAlarmsMessage(); + + mSyncStorageEngine.doDatabaseCleanup(accounts); + + if (hadAccountsAlready && accounts.length > 0) { + // request a sync so that if the password was changed we will + // retry any sync that failed when it was wrong + startSync(null /* all providers */, null /* no extras */); + } + } + private BroadcastReceiver mConnectivityIntentReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { @@ -229,7 +272,11 @@ class SyncManager { private static final String SYNCMANAGER_PREFS_FILENAME = "/data/system/syncmanager.prefs"; + private final boolean mFactoryTest; + public SyncManager(Context context, boolean factoryTest) { + mFactoryTest = factoryTest; + // Initialize the SyncStorageEngine first, before registering observers // and creating threads and so on; it may fail if the disk is full. SyncStorageEngine.init(context); @@ -244,6 +291,8 @@ class SyncManager { mPackageManager = null; + mSyncAdapters = new SyncAdaptersCache(mContext); + mSyncAlarmIntent = PendingIntent.getBroadcast( mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0); @@ -253,6 +302,9 @@ class SyncManager { IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); context.registerReceiver(mConnectivityIntentReceiver, intentFilter); + intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); + context.registerReceiver(mBootCompletedReceiver, intentFilter); + intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW); intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); context.registerReceiver(mStorageIntentReceiver, intentFilter); @@ -288,42 +340,6 @@ class SyncManager { sendCheckAlarmsMessage(); } }); - - if (!factoryTest) { - AccountMonitorListener listener = new AccountMonitorListener() { - public void onAccountsUpdated(String[] accounts) { - final boolean hadAccountsAlready = mAccounts != null; - // copy the accounts into a new array and change mAccounts to point to it - String[] newAccounts = new String[accounts.length]; - System.arraycopy(accounts, 0, newAccounts, 0, accounts.length); - mAccounts = newAccounts; - - // if a sync is in progress yet it is no longer in the accounts list, cancel it - ActiveSyncContext activeSyncContext = mActiveSyncContext; - if (activeSyncContext != null) { - if (!ArrayUtils.contains(newAccounts, - activeSyncContext.mSyncOperation.account)) { - Log.d(TAG, "canceling sync since the account has been removed"); - sendSyncFinishedOrCanceledMessage(activeSyncContext, - null /* no result since this is a cancel */); - } - } - - // we must do this since we don't bother scheduling alarms when - // the accounts are not set yet - sendCheckAlarmsMessage(); - - mSyncStorageEngine.doDatabaseCleanup(accounts); - - if (hadAccountsAlready && mAccounts.length > 0) { - // request a sync so that if the password was changed we will retry any sync - // that failed when it was wrong - startSync(null /* all providers */, null /* no extras */); - } - } - }; - mAccountMonitor = new AccountMonitor(context, listener); - } } private synchronized void initializeSyncPoll() { @@ -452,19 +468,13 @@ class SyncManager { return mSyncStorageEngine; } - private void ensureContentResolver() { - if (mContentResolver == null) { - mContentResolver = mContext.getContentResolver(); - } - } - private void ensureAlarmService() { if (mAlarmService == null) { mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); } } - public String getSyncingAccount() { + public Account getSyncingAccount() { ActiveSyncContext activeSyncContext = mActiveSyncContext; return (activeSyncContext != null) ? activeSyncContext.mSyncOperation.account : null; } @@ -535,10 +545,10 @@ class SyncManager { delay = -1; // this means schedule at the front of the queue } - String[] accounts; - String accountFromExtras = extras.getString(ContentResolver.SYNC_EXTRAS_ACCOUNT); - if (!TextUtils.isEmpty(accountFromExtras)) { - accounts = new String[]{accountFromExtras}; + Account[] accounts; + Account accountFromExtras = extras.getParcelable(ContentResolver.SYNC_EXTRAS_ACCOUNT); + if (accountFromExtras != null) { + accounts = new Account[]{accountFromExtras}; } else { // if the accounts aren't configured yet then we can't support an account-less // sync request @@ -575,20 +585,33 @@ class SyncManager { source = SyncStorageEngine.SOURCE_SERVER; } - List<String> names = new ArrayList<String>(); - List<ProviderInfo> providers = new ArrayList<ProviderInfo>(); - populateProvidersList(url, names, providers); + // compile a list of authorities that have sync adapters + // for each authority sync each account that matches a sync adapter + Set<String> syncableAuthorities = new HashSet<String>(); + for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter : + mSyncAdapters.getAllServices()) { + syncableAuthorities.add(syncAdapter.type.authority); + } - final int numProviders = providers.size(); - for (int i = 0; i < numProviders; i++) { - if (!providers.get(i).isSyncable) continue; - final String name = names.get(i); - for (String account : accounts) { - scheduleSyncOperation(new SyncOperation(account, source, name, extras, delay)); - // TODO: remove this when Calendar supports multiple accounts. Until then - // pretend that only the first account exists when syncing calendar. - if ("calendar".equals(name)) { - break; + // if the url was specified then replace the list of authorities with just this authority + // or clear it if this authority isn't syncable + if (url != null) { + boolean isSyncable = syncableAuthorities.contains(url.getAuthority()); + syncableAuthorities.clear(); + if (isSyncable) syncableAuthorities.add(url.getAuthority()); + } + + for (String authority : syncableAuthorities) { + for (Account account : accounts) { + if (mSyncAdapters.getServiceInfo(new SyncAdapterType(authority, account.mType)) + != null) { + scheduleSyncOperation( + new SyncOperation(account, source, authority, extras, delay)); + // TODO: remove this when Calendar supports multiple accounts. Until then + // pretend that only the first account exists when syncing calendar. + if ("calendar".equals(authority)) { + break; + } } } } @@ -598,32 +621,6 @@ class SyncManager { mStatusText = message; } - private void populateProvidersList(Uri url, List<String> names, List<ProviderInfo> providers) { - try { - final IPackageManager packageManager = getPackageManager(); - if (url == null) { - packageManager.querySyncProviders(names, providers); - } else { - final String authority = url.getAuthority(); - ProviderInfo info = packageManager.resolveContentProvider(url.getAuthority(), 0); - if (info != null) { - // only set this provider if the requested authority is the primary authority - String[] providerNames = info.authority.split(";"); - if (url.getAuthority().equals(providerNames[0])) { - names.add(authority); - providers.add(info); - } - } - } - } catch (RemoteException ex) { - // we should really never get this, but if we do then clear the lists, which - // will result in the dropping of the sync request - Log.e(TAG, "error trying to get the ProviderInfo for " + url, ex); - names.clear(); - providers.clear(); - } - } - public void scheduleLocalSync(Uri url) { final Bundle extras = new Bundle(); extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true); @@ -721,8 +718,7 @@ class SyncManager { } // Cap the delay - ensureContentResolver(); - long maxSyncRetryTimeInSeconds = Settings.Gservices.getLong(mContentResolver, + long maxSyncRetryTimeInSeconds = Settings.Gservices.getLong(mContext.getContentResolver(), Settings.Gservices.SYNC_MAX_RETRY_DELAY_IN_SECONDS, DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS); if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) { @@ -857,7 +853,7 @@ class SyncManager { * Value type that represents a sync operation. */ static class SyncOperation implements Comparable { - final String account; + final Account account; int syncSource; String authority; Bundle extras; @@ -866,7 +862,7 @@ class SyncManager { long delay; SyncStorageEngine.PendingOperation pendingOperation; - SyncOperation(String account, int source, String authority, Bundle extras, long delay) { + SyncOperation(Account account, int source, String authority, Bundle extras, long delay) { this.account = account; this.syncSource = source; this.authority = authority; @@ -937,21 +933,19 @@ class SyncManager { /** * @hide */ - class ActiveSyncContext extends ISyncContext.Stub { + class ActiveSyncContext extends ISyncContext.Stub implements ServiceConnection { final SyncOperation mSyncOperation; final long mHistoryRowId; - final IContentProvider mContentProvider; - final ISyncAdapter mSyncAdapter; + ISyncAdapter mSyncAdapter; final long mStartTime; long mTimeoutStartTime; - public ActiveSyncContext(SyncOperation syncOperation, IContentProvider contentProvider, - ISyncAdapter syncAdapter, long historyRowId) { + public ActiveSyncContext(SyncOperation syncOperation, + long historyRowId) { super(); mSyncOperation = syncOperation; mHistoryRowId = historyRowId; - mContentProvider = contentProvider; - mSyncAdapter = syncAdapter; + mSyncAdapter = null; mStartTime = SystemClock.elapsedRealtime(); mTimeoutStartTime = mStartTime; } @@ -977,6 +971,37 @@ class SyncManager { .append(", syncOperation ").append(mSyncOperation); } + public void onServiceConnected(ComponentName name, IBinder service) { + Message msg = mSyncHandler.obtainMessage(); + msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED; + msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service)); + mSyncHandler.sendMessage(msg); + } + + public void onServiceDisconnected(ComponentName name) { + Message msg = mSyncHandler.obtainMessage(); + msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED; + msg.obj = new ServiceConnectionData(this, null); + mSyncHandler.sendMessage(msg); + } + + boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this); + } + Intent intent = new Intent(); + intent.setAction("android.content.SyncAdapter"); + intent.setComponent(info.componentName); + return mContext.bindService(intent, this, Context.BIND_AUTO_CREATE); + } + + void unBindFromSyncAdapter() { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.d(TAG, "unBindFromSyncAdapter: connection " + this); + } + mContext.unbindService(this); + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -991,6 +1016,12 @@ class SyncManager { if (isSyncEnabled()) { dumpSyncHistory(pw, sb); } + + pw.println(); + pw.println("SyncAdapters:"); + for (RegisteredServicesCache.ServiceInfo info : mSyncAdapters.getAllServices()) { + pw.println(" " + info); + } } static String formatTime(long time) { @@ -1004,7 +1035,7 @@ class SyncManager { pw.print("data connected: "); pw.println(mDataConnectionIsConnected); pw.print("memory low: "); pw.println(mStorageIsLow); - final String[] accounts = mAccounts; + final Account[] accounts = mAccounts; pw.print("accounts: "); if (accounts != null) { pw.println(accounts.length); @@ -1068,7 +1099,8 @@ class SyncManager { for (int i=0; i<N; i++) { SyncStorageEngine.PendingOperation op = ops.get(i); pw.print(" #"); pw.print(i); pw.print(": account="); - pw.print(op.account); pw.print(" authority="); + pw.print(op.account.mName); pw.print(":"); + pw.print(op.account.mType); pw.print(" authority="); pw.println(op.authority); if (op.extras != null && op.extras.size() > 0) { sb.setLength(0); @@ -1078,7 +1110,7 @@ class SyncManager { } } - HashSet<String> processedAccounts = new HashSet<String>(); + HashSet<Account> processedAccounts = new HashSet<Account>(); ArrayList<SyncStatusInfo> statuses = mSyncStorageEngine.getSyncStatus(); if (statuses != null && statuses.size() > 0) { @@ -1090,7 +1122,7 @@ class SyncManager { SyncStorageEngine.AuthorityInfo authority = mSyncStorageEngine.getAuthority(status.authorityId); if (authority != null) { - String curAccount = authority.account; + Account curAccount = authority.account; if (processedAccounts.contains(curAccount)) { continue; @@ -1098,8 +1130,9 @@ class SyncManager { processedAccounts.add(curAccount); - pw.print(" Account "); pw.print(authority.account); - pw.println(":"); + pw.print(" Account "); pw.print(authority.account.mName); + pw.print(" "); pw.print(authority.account.mType); + pw.println(":"); for (int j=i; j<N; j++) { status = statuses.get(j); authority = mSyncStorageEngine.getAuthority(status.authorityId); @@ -1219,9 +1252,15 @@ class SyncManager { SyncStorageEngine.AuthorityInfo authority = mSyncStorageEngine.getAuthority(item.authorityId); pw.print(" #"); pw.print(i+1); pw.print(": "); - pw.print(authority != null ? authority.account : "<no account>"); - pw.print(" "); - pw.print(authority != null ? authority.authority : "<no account>"); + if (authority != null) { + pw.print(authority.account.mName); + pw.print(":"); + pw.print(authority.account.mType); + pw.print(" "); + pw.print(authority.authority); + } else { + pw.print("<no account>"); + } Time time = new Time(); time.set(item.eventTime); pw.print(" "); pw.print(SyncStorageEngine.SOURCES[item.source]); @@ -1278,6 +1317,15 @@ class SyncManager { } } + class ServiceConnectionData { + public final ActiveSyncContext activeSyncContext; + public final ISyncAdapter syncAdapter; + ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) { + this.activeSyncContext = activeSyncContext; + this.syncAdapter = syncAdapter; + } + } + /** * Handles SyncOperation Messages that are posted to the associated * HandlerThread. @@ -1287,6 +1335,8 @@ class SyncManager { private static final int MESSAGE_SYNC_FINISHED = 1; private static final int MESSAGE_SYNC_ALARM = 2; private static final int MESSAGE_CHECK_ALARMS = 3; + private static final int MESSAGE_SERVICE_CONNECTED = 4; + private static final int MESSAGE_SERVICE_DISCONNECTED = 5; public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo(); private Long mAlarmScheduleTime = null; @@ -1301,7 +1351,7 @@ class SyncManager { */ class SyncNotificationInfo { // only valid if isActive is true - public String account; + public Account account; // only valid if isActive is true public String authority; @@ -1358,6 +1408,53 @@ class SyncManager { runStateIdle(); break; + case SyncHandler.MESSAGE_SERVICE_CONNECTED: { + ServiceConnectionData msgData = (ServiceConnectionData)msg.obj; + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " + + msgData.activeSyncContext + + " active is " + mActiveSyncContext); + } + // check that this isn't an old message + if (mActiveSyncContext == msgData.activeSyncContext) { + runBoundToSyncAdapter(msgData.syncAdapter); + } + break; + } + + case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { + ServiceConnectionData msgData = (ServiceConnectionData)msg.obj; + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " + + msgData.activeSyncContext + + " active is " + mActiveSyncContext); + } + // check that this isn't an old message + if (mActiveSyncContext == msgData.activeSyncContext) { + // cancel the sync if we have a syncadapter, which means one is + // outstanding + if (mActiveSyncContext.mSyncAdapter != null) { + try { + mActiveSyncContext.mSyncAdapter.cancelSync(); + } catch (RemoteException e) { + // we don't need to retry this in this case + } + } + + // pretend that the sync failed with an IOException, + // which is a soft error + SyncResult syncResult = new SyncResult(); + syncResult.stats.numIoExceptions++; + runSyncFinishedOrCanceled(syncResult); + + // since we are no longer syncing, check if it is time to start a new + // sync + runStateIdle(); + } + + break; + } + case SyncHandler.MESSAGE_SYNC_ALARM: { boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); if (isLoggable) { @@ -1456,7 +1553,7 @@ class SyncManager { // If the accounts aren't known yet then we aren't ready to run. We will be kicked // when the account lookup request does complete. - String[] accounts = mAccounts; + Account[] accounts = mAccounts; if (accounts == null) { if (isLoggable) { Log.v(TAG, "runStateIdle: accounts not known, skipping"); @@ -1542,67 +1639,68 @@ class SyncManager { mSyncQueue.popHead(); } - String providerName = syncOperation.authority; - ensureContentResolver(); - IContentProvider contentProvider; - - // acquire the provider and update the sync history - try { - contentProvider = mContentResolver.acquireProvider(providerName); - if (contentProvider == null) { - Log.e(TAG, "Provider " + providerName + " doesn't exist"); - return; - } - if (contentProvider.getSyncAdapter() == null) { - Log.e(TAG, "Provider " + providerName + " isn't syncable, " + contentProvider); - return; + // connect to the sync adapter + SyncAdapterType syncAdapterType = new SyncAdapterType(syncOperation.authority, + syncOperation.account.mType); + RegisteredServicesCache.ServiceInfo syncAdapterInfo = + mSyncAdapters.getServiceInfo(syncAdapterType); + if (syncAdapterInfo == null) { + if (Config.LOGD) { + Log.d(TAG, "can't find a sync adapter for " + syncAdapterType); } - } catch (RemoteException remoteExc) { - Log.e(TAG, "Caught a RemoteException while preparing for sync, rescheduling " - + syncOperation, remoteExc); - rescheduleWithDelay(syncOperation); + runStateIdle(); return; - } catch (RuntimeException exc) { - Log.e(TAG, "Caught a RuntimeException while validating sync of " + providerName, - exc); + } + + ActiveSyncContext activeSyncContext = + new ActiveSyncContext(syncOperation, insertStartSyncEvent(syncOperation)); + mActiveSyncContext = activeSyncContext; + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "runStateIdle: setting mActiveSyncContext to " + mActiveSyncContext); + } + mSyncStorageEngine.setActiveSync(mActiveSyncContext); + if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo)) { + Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo); + mActiveSyncContext = null; + mSyncStorageEngine.setActiveSync(mActiveSyncContext); + runStateIdle(); return; } - final long historyRowId = insertStartSyncEvent(syncOperation); + mSyncWakeLock.acquire(); + // no need to schedule an alarm, as that will be done by our caller. + + // the next step will occur when we get either a timeout or a + // MESSAGE_SERVICE_CONNECTED or MESSAGE_SERVICE_DISCONNECTED message + } + private void runBoundToSyncAdapter(ISyncAdapter syncAdapter) { + mActiveSyncContext.mSyncAdapter = syncAdapter; + final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation; try { - ISyncAdapter syncAdapter = contentProvider.getSyncAdapter(); - ActiveSyncContext activeSyncContext = new ActiveSyncContext(syncOperation, - contentProvider, syncAdapter, historyRowId); - mSyncWakeLock.acquire(); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "starting sync of " + syncOperation); - } - syncAdapter.startSync(activeSyncContext, syncOperation.account, + syncAdapter.startSync(mActiveSyncContext, syncOperation.account, syncOperation.extras); - mActiveSyncContext = activeSyncContext; - mSyncStorageEngine.setActiveSync(mActiveSyncContext); } catch (RemoteException remoteExc) { if (Config.LOGD) { Log.d(TAG, "runStateIdle: caught a RemoteException, rescheduling", remoteExc); } + mActiveSyncContext.unBindFromSyncAdapter(); mActiveSyncContext = null; mSyncStorageEngine.setActiveSync(mActiveSyncContext); rescheduleWithDelay(syncOperation); } catch (RuntimeException exc) { + mActiveSyncContext.unBindFromSyncAdapter(); mActiveSyncContext = null; mSyncStorageEngine.setActiveSync(mActiveSyncContext); Log.e(TAG, "Caught a RuntimeException while starting the sync " + syncOperation, exc); } - - // no need to schedule an alarm, as that will be done by our caller. } private void runSyncFinishedOrCanceled(SyncResult syncResult) { boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); if (isLoggable) Log.v(TAG, "runSyncFinishedOrCanceled"); - ActiveSyncContext activeSyncContext = mActiveSyncContext; + final ActiveSyncContext activeSyncContext = mActiveSyncContext; mActiveSyncContext = null; mSyncStorageEngine.setActiveSync(mActiveSyncContext); @@ -1642,10 +1740,12 @@ class SyncManager { Log.v(TAG, "runSyncFinishedOrCanceled: is a cancel: operation " + syncOperation); } - try { - activeSyncContext.mSyncAdapter.cancelSync(); - } catch (RemoteException e) { - // we don't need to retry this in this case + if (activeSyncContext.mSyncAdapter != null) { + try { + activeSyncContext.mSyncAdapter.cancelSync(); + } catch (RemoteException e) { + // we don't need to retry this in this case + } } historyMessage = SyncStorageEngine.MESG_CANCELED; downstreamActivity = 0; @@ -1655,7 +1755,7 @@ class SyncManager { stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, upstreamActivity, downstreamActivity, elapsedTime); - mContentResolver.releaseProvider(activeSyncContext.mContentProvider); + activeSyncContext.unBindFromSyncAdapter(); if (syncResult != null && syncResult.tooManyDeletions) { installHandleTooManyDeletesNotification(syncOperation.account, @@ -1860,7 +1960,7 @@ class SyncManager { mContext.sendBroadcast(syncStateIntent); } - private void installHandleTooManyDeletesNotification(String account, String authority, + private void installHandleTooManyDeletesNotification(Account account, String authority, long numDeletes) { if (mNotificationMgr == null) return; Intent clickIntent = new Intent(); @@ -2071,7 +2171,7 @@ class SyncManager { if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(true /* check the DB */); } - public void clear(String account, String authority) { + public void clear(Account account, String authority) { Iterator<Map.Entry<String, SyncOperation>> entries = mOpsByKey.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<String, SyncOperation> entry = entries.next(); |