diff options
author | Matthew Williams <mjwilliams@google.com> | 2013-09-06 16:06:17 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-09-06 16:06:17 +0000 |
commit | abc1d89950aec7efed2e39dee7b13453050d1d5a (patch) | |
tree | ff2b65dae93f8827f6e533b550bf6cb725eb1145 /services | |
parent | a2bcee619d91c9ff4821a85c9ea72b6391219309 (diff) | |
parent | 56dbf8f23677d28615e61ef2fbb0e738cca02528 (diff) | |
download | frameworks_base-abc1d89950aec7efed2e39dee7b13453050d1d5a.zip frameworks_base-abc1d89950aec7efed2e39dee7b13453050d1d5a.tar.gz frameworks_base-abc1d89950aec7efed2e39dee7b13453050d1d5a.tar.bz2 |
Merge "Fix broken javadocs"
Diffstat (limited to 'services')
6 files changed, 2023 insertions, 1428 deletions
diff --git a/services/java/com/android/server/content/ContentService.java b/services/java/com/android/server/content/ContentService.java index a56af08..ab20461 100644 --- a/services/java/com/android/server/content/ContentService.java +++ b/services/java/com/android/server/content/ContentService.java @@ -25,6 +25,8 @@ import android.content.Context; import android.content.IContentService; import android.content.ISyncStatusObserver; import android.content.PeriodicSync; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; import android.content.SyncAdapterType; import android.content.SyncInfo; import android.content.SyncRequest; @@ -315,7 +317,6 @@ public final class ContentService extends IContentService.Stub { } } - @Override public void requestSync(Account account, String authority, Bundle extras) { ContentResolver.validateSyncExtrasBundle(extras); int userId = UserHandle.getCallingUserId(); @@ -345,61 +346,56 @@ public final class ContentService extends IContentService.Stub { * Depending on the request, we enqueue to suit in the SyncManager. * @param request */ - @Override public void sync(SyncRequest request) { Bundle extras = request.getBundle(); ContentResolver.validateSyncExtrasBundle(extras); - long flextime = request.getSyncFlexTime(); - long runAtTime = request.getSyncRunTime(); int userId = UserHandle.getCallingUserId(); - int uId = Binder.getCallingUid(); - + int callerUid = Binder.getCallingUid(); // This makes it so that future permission checks will be in the context of this // process rather than the caller's process. We will restore this before returning. long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); - if (syncManager != null) { + if (syncManager == null) return; + + long flextime = request.getSyncFlexTime(); + long runAtTime = request.getSyncRunTime(); + if (request.isPeriodic()) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.WRITE_SYNC_SETTINGS, + "no permission to write the sync settings"); + SyncStorageEngine.EndPoint info; + if (!request.hasAuthority()) { + // Extra permissions checking for sync service. + verifySignatureForPackage(callerUid, + request.getService().getPackageName(), "sync"); + info = new SyncStorageEngine.EndPoint(request.getService(), userId); + } else { + info = new SyncStorageEngine.EndPoint( + request.getAccount(), request.getProvider(), userId); + } + if (runAtTime < 60) { + Slog.w(TAG, "Requested poll frequency of " + runAtTime + + " seconds being rounded up to 60 seconds."); + runAtTime = 60; + } + // Schedule periodic sync. + getSyncManager().getSyncStorageEngine() + .updateOrAddPeriodicSync(info, runAtTime, flextime, extras); + } else { + long beforeRuntimeMillis = (flextime) * 1000; + long runtimeMillis = runAtTime * 1000; if (request.hasAuthority()) { - // Sync Adapter registered with the system - old API. - final Account account = request.getProviderInfo().first; - final String provider = request.getProviderInfo().second; - if (request.isPeriodic()) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.WRITE_SYNC_SETTINGS, - "no permission to write the sync settings"); - if (runAtTime < 60) { - Slog.w(TAG, "Requested poll frequency of " + runAtTime - + " seconds being rounded up to 60 seconds."); - runAtTime = 60; - } - PeriodicSync syncToAdd = - new PeriodicSync(account, provider, extras, runAtTime, flextime); - getSyncManager().getSyncStorageEngine().addPeriodicSync(syncToAdd, userId); - } else { - long beforeRuntimeMillis = (flextime) * 1000; - long runtimeMillis = runAtTime * 1000; - syncManager.scheduleSync( - account, userId, uId, provider, extras, - beforeRuntimeMillis, runtimeMillis, - false /* onlyThoseWithUnknownSyncableState */); - } + syncManager.scheduleSync( + request.getAccount(), userId, callerUid, request.getProvider(), extras, + beforeRuntimeMillis, runtimeMillis, + false /* onlyThoseWithUnknownSyncableState */); } else { - // Anonymous sync - new API. - final ComponentName syncService = request.getService(); - if (request.isPeriodic()) { - throw new RuntimeException("Periodic anonymous syncs not implemented yet."); - } else { - long beforeRuntimeMillis = (flextime) * 1000; - long runtimeMillis = runAtTime * 1000; - syncManager.scheduleSync( - syncService, userId, uId, extras, - beforeRuntimeMillis, - runtimeMillis, - false /* onlyThoseWithUnknownSyncableState */); // Empty function. - throw new RuntimeException("One-off anonymous syncs not implemented yet."); - } + syncManager.scheduleSync( + request.getService(), userId, callerUid, extras, + beforeRuntimeMillis, + runtimeMillis); // Empty function. } } } finally { @@ -410,11 +406,13 @@ public final class ContentService extends IContentService.Stub { /** * Clear all scheduled sync operations that match the uri and cancel the active sync * if they match the authority and account, if they are present. - * @param account filter the pending and active syncs to cancel using this account - * @param authority filter the pending and active syncs to cancel using this authority + * + * @param account filter the pending and active syncs to cancel using this account, or null. + * @param authority filter the pending and active syncs to cancel using this authority, or + * null. + * @param cname cancel syncs running on this service, or null for provider/account. */ - @Override - public void cancelSync(Account account, String authority) { + public void cancelSync(Account account, String authority, ComponentName cname) { int userId = UserHandle.getCallingUserId(); // This makes it so that future permission checks will be in the context of this @@ -423,14 +421,54 @@ public final class ContentService extends IContentService.Stub { try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - syncManager.clearScheduledSyncOperations(account, userId, authority); - syncManager.cancelActiveSync(account, userId, authority); + SyncStorageEngine.EndPoint info; + if (cname == null) { + info = new SyncStorageEngine.EndPoint(account, authority, userId); + } else { + info = new SyncStorageEngine.EndPoint(cname, userId); + } + syncManager.clearScheduledSyncOperations(info); + syncManager.cancelActiveSync(info, null /* all syncs for this adapter */); } } finally { restoreCallingIdentity(identityToken); } } + public void cancelRequest(SyncRequest request) { + SyncManager syncManager = getSyncManager(); + if (syncManager == null) return; + int userId = UserHandle.getCallingUserId(); + int callerUid = Binder.getCallingUid(); + + long identityToken = clearCallingIdentity(); + try { + SyncStorageEngine.EndPoint info; + Bundle extras = new Bundle(request.getBundle()); + if (request.hasAuthority()) { + Account account = request.getAccount(); + String provider = request.getProvider(); + info = new SyncStorageEngine.EndPoint(account, provider, userId); + } else { + // Only allowed to manipulate syncs for a service which you own. + ComponentName service = request.getService(); + verifySignatureForPackage(callerUid, service.getPackageName(), "cancel"); + info = new SyncStorageEngine.EndPoint(service, userId); + } + if (request.isPeriodic()) { + // Remove periodic sync. + mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, + "no permission to write the sync settings"); + getSyncManager().getSyncStorageEngine().removePeriodicSync(info, extras); + } + // Cancel active syncs and clear pending syncs from the queue. + syncManager.cancelScheduledSyncOperation(info, extras); + syncManager.cancelActiveSync(info, extras); + } finally { + restoreCallingIdentity(identityToken); + } + } + /** * Get information about the SyncAdapters that are known to the system. * @return an array of SyncAdapters that have registered with the system @@ -459,8 +497,8 @@ public final class ContentService extends IContentService.Stub { try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - return syncManager.getSyncStorageEngine().getSyncAutomatically( - account, userId, providerName); + return syncManager.getSyncStorageEngine() + .getSyncAutomatically(account, userId, providerName); } } finally { restoreCallingIdentity(identityToken); @@ -478,18 +516,15 @@ public final class ContentService extends IContentService.Stub { try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - syncManager.getSyncStorageEngine().setSyncAutomatically( - account, userId, providerName, sync); + syncManager.getSyncStorageEngine() + .setSyncAutomatically(account, userId, providerName, sync); } } finally { restoreCallingIdentity(identityToken); } } - /** - * Old API. Schedule periodic sync with default flex time. - */ - @Override + /** Old API. Schedule periodic sync with default flex time. */ public void addPeriodicSync(Account account, String authority, Bundle extras, long pollFrequency) { mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, @@ -501,21 +536,22 @@ public final class ContentService extends IContentService.Stub { + " seconds being rounded up to 60 seconds."); pollFrequency = 60; } + long defaultFlex = SyncStorageEngine.calculateDefaultFlexTime(pollFrequency); long identityToken = clearCallingIdentity(); try { - // Add default flex time to this sync. - PeriodicSync syncToAdd = - new PeriodicSync(account, authority, extras, - pollFrequency, - SyncStorageEngine.calculateDefaultFlexTime(pollFrequency)); - getSyncManager().getSyncStorageEngine().addPeriodicSync(syncToAdd, userId); + SyncStorageEngine.EndPoint info = + new SyncStorageEngine.EndPoint(account, authority, userId); + getSyncManager().getSyncStorageEngine() + .updateOrAddPeriodicSync(info, + pollFrequency, + defaultFlex, + extras); } finally { restoreCallingIdentity(identityToken); } } - @Override public void removePeriodicSync(Account account, String authority, Bundle extras) { mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, "no permission to write the sync settings"); @@ -523,32 +559,34 @@ public final class ContentService extends IContentService.Stub { long identityToken = clearCallingIdentity(); try { - PeriodicSync syncToRemove = new PeriodicSync(account, authority, extras, - 0 /* Not read for removal */, 0 /* Not read for removal */); - getSyncManager().getSyncStorageEngine().removePeriodicSync(syncToRemove, userId); + getSyncManager().getSyncStorageEngine() + .removePeriodicSync( + new SyncStorageEngine.EndPoint(account, authority, userId), + extras); } finally { restoreCallingIdentity(identityToken); } } - /** - * TODO: Implement. - * @param request Sync to remove. - */ - public void removeSync(SyncRequest request) { - - } - - @Override - public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) { + public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName, + ComponentName cname) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, "no permission to read the sync settings"); int userId = UserHandle.getCallingUserId(); + int callerUid = Binder.getCallingUid(); long identityToken = clearCallingIdentity(); try { - return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( - account, userId, providerName); + if (cname == null) { + return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( + new SyncStorageEngine.EndPoint(account, providerName, userId)); + } else if (account == null && providerName == null) { + verifySignatureForPackage(callerUid, cname.getPackageName(), "getPeriodicSyncs"); + return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( + new SyncStorageEngine.EndPoint(cname, userId)); + } else { + throw new IllegalArgumentException("Invalid authority specified"); + } } finally { restoreCallingIdentity(identityToken); } @@ -572,7 +610,6 @@ public final class ContentService extends IContentService.Stub { return -1; } - @Override public void setIsSyncable(Account account, String providerName, int syncable) { mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, "no permission to write the sync settings"); @@ -590,6 +627,43 @@ public final class ContentService extends IContentService.Stub { } } + public void setServiceActive(ComponentName cname, boolean active) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, + "no permission to write the sync settings"); + verifySignatureForPackage(Binder.getCallingUid(), cname.getPackageName(), "setIsEnabled"); + + int userId = UserHandle.getCallingUserId(); + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + int syncable = active ? 1 : 0; + syncManager.getSyncStorageEngine().setIsEnabled( + cname, userId, syncable); + } + } finally { + restoreCallingIdentity(identityToken); + } + } + + public boolean isServiceActive(ComponentName cname) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, + "no permission to read the sync settings"); + verifySignatureForPackage(Binder.getCallingUid(), cname.getPackageName(), "getIsEnabled"); + + int userId = UserHandle.getCallingUserId(); + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + return (syncManager.getIsTargetServiceActive(cname, userId) == 1); + } + } finally { + restoreCallingIdentity(identityToken); + } + return false; + } + @Override public boolean getMasterSyncAutomatically() { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, @@ -625,17 +699,24 @@ public final class ContentService extends IContentService.Stub { } } - public boolean isSyncActive(Account account, String authority) { + public boolean isSyncActive(Account account, String authority, ComponentName cname) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, "no permission to read the sync stats"); int userId = UserHandle.getCallingUserId(); - + int callingUid = Binder.getCallingUid(); long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); - if (syncManager != null) { + if (syncManager == null) { + return false; + } + if (cname == null) { return syncManager.getSyncStorageEngine().isSyncActive( - account, userId, authority); + new SyncStorageEngine.EndPoint(account, authority, userId)); + } else if (account == null && authority == null) { + verifySignatureForPackage(callingUid, cname.getPackageName(), "isSyncActive"); + return syncManager.getSyncStorageEngine().isSyncActive( + new SyncStorageEngine.EndPoint(cname, userId)); } } finally { restoreCallingIdentity(identityToken); @@ -656,39 +737,55 @@ public final class ContentService extends IContentService.Stub { } } - public SyncStatusInfo getSyncStatus(Account account, String authority) { + public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, "no permission to read the sync stats"); int userId = UserHandle.getCallingUserId(); - + int callerUid = Binder.getCallingUid(); long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority( - account, userId, authority); + if (syncManager == null) { + return null; + } + SyncStorageEngine.EndPoint info; + if (cname == null) { + info = new SyncStorageEngine.EndPoint(account, authority, userId); + } else if (account == null && authority == null) { + verifySignatureForPackage(callerUid, cname.getPackageName(), "getSyncStatus"); + info = new SyncStorageEngine.EndPoint(cname, userId); + } else { + throw new IllegalArgumentException("Must call sync status with valid authority"); } + return syncManager.getSyncStorageEngine().getStatusByAuthority(info); } finally { restoreCallingIdentity(identityToken); } - return null; } - public boolean isSyncPending(Account account, String authority) { + public boolean isSyncPending(Account account, String authority, ComponentName cname) { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, "no permission to read the sync stats"); int userId = UserHandle.getCallingUserId(); - + int callerUid = Binder.getCallingUid(); long identityToken = clearCallingIdentity(); + SyncManager syncManager = getSyncManager(); + if (syncManager == null) return false; + try { - SyncManager syncManager = getSyncManager(); - if (syncManager != null) { - return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority); + SyncStorageEngine.EndPoint info; + if (cname == null) { + info = new SyncStorageEngine.EndPoint(account, authority, userId); + } else if (account == null && authority == null) { + verifySignatureForPackage(callerUid, cname.getPackageName(), "isSyncPending"); + info = new SyncStorageEngine.EndPoint(cname, userId); + } else { + throw new IllegalArgumentException("Invalid authority specified"); } + return syncManager.getSyncStorageEngine().isSyncPending(info); } finally { restoreCallingIdentity(identityToken); } - return false; } public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { @@ -722,6 +819,30 @@ public final class ContentService extends IContentService.Stub { } /** + * Helper to verify that the provided package name shares the same cert as the caller. + * @param callerUid uid of the calling process. + * @param packageName package to verify against package of calling application. + * @param tag a tag to use when throwing an exception if the signatures don't + * match. Cannot be null. + * @return true if the calling application and the provided package are signed with the same + * certificate. + */ + private boolean verifySignatureForPackage(int callerUid, String packageName, String tag) { + PackageManager pm = mContext.getPackageManager(); + try { + int serviceUid = pm.getApplicationInfo(packageName, 0).uid; + if (pm.checkSignatures(callerUid, serviceUid) == PackageManager.SIGNATURE_MATCH) { + return true; + } else { + throw new SecurityException(tag + ": Caller certificate does not match that for - " + + packageName); + } + } catch (PackageManager.NameNotFoundException e) { + throw new IllegalArgumentException(tag + ": " + packageName + " package not found."); + } + } + + /** * Hide this class since it is not part of api, * but current unittest framework requires it to be public * @hide diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java index a6b69a2..3802415 100644 --- a/services/java/com/android/server/content/SyncManager.java +++ b/services/java/com/android/server/content/SyncManager.java @@ -31,6 +31,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.ISyncAdapter; import android.content.ISyncContext; +import android.content.ISyncServiceAdapter; import android.content.ISyncStatusObserver; import android.content.Intent; import android.content.IntentFilter; @@ -200,8 +201,9 @@ public class SyncManager { Log.v(TAG, "Internal storage is low."); } mStorageIsLow = true; - cancelActiveSync(null /* any account */, UserHandle.USER_ALL, - null /* any authority */); + cancelActiveSync( + SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL, + null /* any sync */); } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Internal storage is ok."); @@ -219,19 +221,6 @@ public class SyncManager { } }; - private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (getConnectivityManager().getBackgroundDataSetting()) { - scheduleSync(null /* account */, UserHandle.USER_ALL, - SyncOperation.REASON_BACKGROUND_DATA_SETTINGS_CHANGED, - null /* authority */, - new Bundle(), 0 /* delay */, 0 /* delay */, - false /* onlyThoseWithUnknownSyncableState */); - } - } - }; - private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -276,16 +265,16 @@ public class SyncManager { doDatabaseCleanup(); } + AccountAndUser[] accounts = mRunningAccounts; for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { - if (!containsAccountAndUser(mRunningAccounts, - currentSyncContext.mSyncOperation.account, - currentSyncContext.mSyncOperation.userId)) { + if (!containsAccountAndUser(accounts, + currentSyncContext.mSyncOperation.target.account, + currentSyncContext.mSyncOperation.target.userId)) { Log.d(TAG, "canceling sync since the account is no longer running"); sendSyncFinishedOrCanceledMessage(currentSyncContext, 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(); @@ -380,12 +369,17 @@ public class SyncManager { mSyncStorageEngine = SyncStorageEngine.getSingleton(); mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() { @Override - public void onSyncRequest(Account account, int userId, int reason, String authority, - Bundle extras) { - scheduleSync(account, userId, reason, authority, extras, - 0 /* no delay */, - 0 /* no delay */, - false); + public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) { + if (info.target_provider) { + scheduleSync(info.account, info.userId, reason, info.provider, extras, + 0 /* no flex */, + 0 /* run immediately */, + false); + } else if (info.target_service) { + scheduleSync(info.service, info.userId, reason, extras, + 0 /* no flex */, + 0 /* run immediately */); + } } }); @@ -417,9 +411,6 @@ public class SyncManager { context.registerReceiver(mBootCompletedReceiver, intentFilter); } - intentFilter = new IntentFilter(ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); - context.registerReceiver(mBackgroundDataSettingChanged, intentFilter); - intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW); intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); context.registerReceiver(mStorageIntentReceiver, intentFilter); @@ -532,181 +523,106 @@ public class SyncManager { } } + /** + * TODO: Decide if restricted users have different sync options for the sync service (as is + * the case with sync adapters). + */ + public int getIsTargetServiceActive(ComponentName cname, int userId) { + return mSyncStorageEngine.getIsTargetServiceActive(cname, userId); + } + private void ensureAlarmService() { if (mAlarmService == null) { - mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); } } /** * Initiate a sync using the new anonymous service API. - * TODO: Implement. * @param cname SyncService component bound to in order to perform the sync. * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL, * then all users' accounts are considered. * @param uid Linux uid of the application that is performing the sync. * @param extras a Map of SyncAdapter-specific information to control * syncs of a specific provider. Can be null. - * @param beforeRunTimeMillis - * @param runtimeMillis + * @param beforeRunTimeMillis milliseconds before <code>runtimeMillis</code> that this sync may + * be run. + * @param runtimeMillis milliseconds from now by which this sync must be run. */ public void scheduleSync(ComponentName cname, int userId, int uid, Bundle extras, - long beforeRunTimeMillis, long runtimeMillis, - boolean onlyThoseWithUnknownSyncableState) { -/** + long beforeRunTimeMillis, long runtimeMillis) { boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - - final boolean backgroundDataUsageAllowed = !mBootCompleted || - getConnectivityManager().getBackgroundDataSetting(); - - if (extras == null) { - extras = new Bundle(); - } if (isLoggable) { - Log.e(TAG, requestedAccount + " " + extras.toString() + " " + requestedAuthority); + Log.d(TAG, "one off sync for: " + cname + " " + extras.toString()); } + Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false); if (expedited) { runtimeMillis = -1; // this means schedule at the front of the queue } - AccountAndUser[] accounts; - if (requestedAccount != null && userId != UserHandle.USER_ALL) { - accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) }; - } else { - // if the accounts aren't configured yet then we can't support an account-less - // sync request - accounts = mRunningAccounts; - if (accounts.length == 0) { - if (isLoggable) { - Log.v(TAG, "scheduleSync: no accounts configured, dropping"); - } - return; - } - } - - final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false); - final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); - if (manualSync) { - extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); - } final boolean ignoreSettings = extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false); - - int source; - if (uploadOnly) { - source = SyncStorageEngine.SOURCE_LOCAL; - } else if (manualSync) { - source = SyncStorageEngine.SOURCE_USER; - } else if (requestedAuthority == null) { - source = SyncStorageEngine.SOURCE_POLL; - } else { - // this isn't strictly server, since arbitrary callers can (and do) request - // a non-forced two-way sync on a specific url - source = SyncStorageEngine.SOURCE_SERVER; + int source = SyncStorageEngine.SOURCE_SERVICE; + int isEnabled = getIsTargetServiceActive(cname, userId); + // Only schedule this sync if + // - we've explicitly been told to ignore settings. + // - global sync is enabled for this user. + boolean syncAllowed = + ignoreSettings + || mSyncStorageEngine.getMasterSyncAutomatically(userId); + if (!syncAllowed) { + if (isLoggable) { + Log.d(TAG, "scheduleSync: sync of " + cname + " not allowed, dropping request."); + } + return; } - - for (AccountAndUser account : accounts) { - // Compile a list of authorities that have sync adapters. - // For each authority sync each account that matches a sync adapter. - final HashSet<String> syncableAuthorities = new HashSet<String>(); - for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter : - mSyncAdapters.getAllServices(account.userId)) { - syncableAuthorities.add(syncAdapter.type.authority); + if (isEnabled == 0) { + if (isLoggable) { + Log.d(TAG, "scheduleSync: " + cname + " is not enabled, dropping request"); + } + return; + } + SyncStorageEngine.EndPoint info = new SyncStorageEngine.EndPoint(cname, userId); + Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info); + long delayUntil = mSyncStorageEngine.getDelayUntilTime(info); + final long backoffTime = backoff != null ? backoff.first : 0; + if (isEnabled < 0) { + // Initialisation sync. + Bundle newExtras = new Bundle(); + newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); + if (isLoggable) { + Log.v(TAG, "schedule initialisation Sync:" + + ", delay until " + delayUntil + + ", run now " + + ", source " + source + + ", sync service " + cname + + ", extras " + newExtras); } - - // 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 (requestedAuthority != null) { - final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority); - syncableAuthorities.clear(); - if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority); + scheduleSyncOperation( + new SyncOperation(cname, userId, uid, source, newExtras, + 0 /* runtime */, + 0 /* flextime */, + backoffTime, + delayUntil)); + } else { + if (isLoggable) { + Log.v(TAG, "schedule Sync:" + + ", delay until " + delayUntil + + ", run by " + runtimeMillis + + ", flex " + beforeRunTimeMillis + + ", source " + source + + ", sync service " + cname + + ", extras " + extras); } + scheduleSyncOperation( + new SyncOperation(cname, userId, uid, source, extras, + runtimeMillis /* runtime */, + beforeRunTimeMillis /* flextime */, + backoffTime, + delayUntil)); + } - for (String authority : syncableAuthorities) { - int isSyncable = getIsSyncable(account.account, account.userId, - authority); - if (isSyncable == 0) { - continue; - } - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(authority, account.account.type), account.userId); - if (syncAdapterInfo == null) { - continue; - } - final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs(); - final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable(); - if (isSyncable < 0 && isAlwaysSyncable) { - mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1); - isSyncable = 1; - } - if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) { - continue; - } - if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) { - continue; - } - - // always allow if the isSyncable state is unknown - boolean syncAllowed = - (isSyncable < 0) - || ignoreSettings - || (backgroundDataUsageAllowed - && mSyncStorageEngine.getMasterSyncAutomatically(account.userId) - && mSyncStorageEngine.getSyncAutomatically(account.account, - account.userId, authority)); - if (!syncAllowed) { - if (isLoggable) { - Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority - + " is not allowed, dropping request"); - } - continue; - } - - Pair<Long, Long> backoff = mSyncStorageEngine - .getBackoff(account.account, account.userId, authority); - long delayUntil = mSyncStorageEngine.getDelayUntilTime(account.account, - account.userId, authority); - final long backoffTime = backoff != null ? backoff.first : 0; - if (isSyncable < 0) { - // Initialisation sync. - Bundle newExtras = new Bundle(); - newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); - if (isLoggable) { - Log.v(TAG, "schedule initialisation Sync:" - + ", delay until " + delayUntil - + ", run by " + 0 - + ", source " + source - + ", account " + account - + ", authority " + authority - + ", extras " + newExtras); - } - scheduleSyncOperation( - new SyncOperation(account.account, account.userId, reason, source, - authority, newExtras, 0 /* immediate , 0 /* No flex time, - backoffTime, delayUntil, allowParallelSyncs)); - } - if (!onlyThoseWithUnkownSyncableState) { - if (isLoggable) { - Log.v(TAG, "scheduleSync:" - + " delay until " + delayUntil - + " run by " + runtimeMillis - + " flex " + beforeRuntimeMillis - + ", source " + source - + ", account " + account - + ", authority " + authority - + ", extras " + extras); - } - scheduleSyncOperation( - new SyncOperation(account.account, account.userId, reason, source, - authority, extras, runtimeMillis, beforeRuntimeMillis, - backoffTime, delayUntil, allowParallelSyncs)); - } - } - }*/ } /** @@ -755,9 +671,6 @@ public class SyncManager { long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) { boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - final boolean backgroundDataUsageAllowed = !mBootCompleted || - getConnectivityManager().getBackgroundDataSetting(); - if (extras == null) { extras = new Bundle(); } @@ -774,8 +687,6 @@ public class SyncManager { if (requestedAccount != null && userId != UserHandle.USER_ALL) { accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) }; } else { - // if the accounts aren't configured yet then we can't support an account-less - // sync request accounts = mRunningAccounts; if (accounts.length == 0) { if (isLoggable) { @@ -850,12 +761,10 @@ public class SyncManager { continue; } - // always allow if the isSyncable state is unknown boolean syncAllowed = - (isSyncable < 0) + (isSyncable < 0) // always allow if the isSyncable state is unknown || ignoreSettings - || (backgroundDataUsageAllowed - && mSyncStorageEngine.getMasterSyncAutomatically(account.userId) + || (mSyncStorageEngine.getMasterSyncAutomatically(account.userId) && mSyncStorageEngine.getSyncAutomatically(account.account, account.userId, authority)); if (!syncAllowed) { @@ -865,11 +774,12 @@ public class SyncManager { } continue; } - - Pair<Long, Long> backoff = mSyncStorageEngine - .getBackoff(account.account, account.userId, authority); - long delayUntil = mSyncStorageEngine.getDelayUntilTime(account.account, - account.userId, authority); + SyncStorageEngine.EndPoint info = + new SyncStorageEngine.EndPoint( + account.account, authority, account.userId); + Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info); + long delayUntil = + mSyncStorageEngine.getDelayUntilTime(info); final long backoffTime = backoff != null ? backoff.first : 0; if (isSyncable < 0) { // Initialisation sync. @@ -879,6 +789,7 @@ public class SyncManager { Log.v(TAG, "schedule initialisation Sync:" + ", delay until " + delayUntil + ", run by " + 0 + + ", flex " + 0 + ", source " + source + ", account " + account + ", authority " + authority @@ -954,13 +865,12 @@ public class SyncManager { mSyncHandler.sendMessage(msg); } - private void sendCancelSyncsMessage(final Account account, final int userId, - final String authority) { + private void sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras) { if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL"); Message msg = mSyncHandler.obtainMessage(); msg.what = SyncHandler.MESSAGE_CANCEL; - msg.obj = Pair.create(account, authority); - msg.arg1 = userId; + msg.setData(extras); + msg.obj = info; mSyncHandler.sendMessage(msg); } @@ -983,10 +893,11 @@ public class SyncManager { } private void clearBackoffSetting(SyncOperation op) { - mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, - SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); + mSyncStorageEngine.setBackoff(op.target, + SyncStorageEngine.NOT_IN_BACKOFF_MODE, + SyncStorageEngine.NOT_IN_BACKOFF_MODE); synchronized (mSyncQueue) { - mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, 0); + mSyncQueue.onBackoffChanged(op.target, 0); } } @@ -996,7 +907,7 @@ public class SyncManager { final long now = SystemClock.elapsedRealtime(); final Pair<Long, Long> previousSettings = - mSyncStorageEngine.getBackoff(op.account, op.userId, op.authority); + mSyncStorageEngine.getBackoff(op.target); long newDelayInMs = -1; if (previousSettings != null) { // don't increase backoff before current backoff is expired. This will happen for op's @@ -1027,14 +938,12 @@ public class SyncManager { final long backoff = now + newDelayInMs; - mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, - backoff, newDelayInMs); - + mSyncStorageEngine.setBackoff(op.target, backoff, newDelayInMs); op.backoff = backoff; op.updateEffectiveRunTime(); synchronized (mSyncQueue) { - mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, backoff); + mSyncQueue.onBackoffChanged(op.target, backoff); } } @@ -1047,20 +956,20 @@ public class SyncManager { } else { newDelayUntilTime = 0; } - mSyncStorageEngine - .setDelayUntilTime(op.account, op.userId, op.authority, newDelayUntilTime); + mSyncStorageEngine.setDelayUntilTime(op.target, newDelayUntilTime); synchronized (mSyncQueue) { - mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime); + mSyncQueue.onDelayUntilTimeChanged(op.target, newDelayUntilTime); } } /** * Cancel the active sync if it matches the authority and account. - * @param account limit the cancelations to syncs with this account, if non-null - * @param authority limit the cancelations to syncs with this authority, if non-null + * @param info object containing info about which syncs to cancel. The authority can + * have null account/provider info to specify all accounts/providers. + * @param extras if non-null, specifies the exact sync to remove. */ - public void cancelActiveSync(Account account, int userId, String authority) { - sendCancelSyncsMessage(account, userId, authority); + public void cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras) { + sendCancelSyncsMessage(info, extras); } /** @@ -1089,24 +998,40 @@ public class SyncManager { /** * Remove scheduled sync operations. - * @param account limit the removals to operations with this account, if non-null - * @param authority limit the removals to operations with this authority, if non-null + * @param info limit the removals to operations that match this authority. The authority can + * have null account/provider info to specify all accounts/providers. */ - public void clearScheduledSyncOperations(Account account, int userId, String authority) { + public void clearScheduledSyncOperations(SyncStorageEngine.EndPoint info) { synchronized (mSyncQueue) { - mSyncQueue.remove(account, userId, authority); + mSyncQueue.remove(info, null /* all operations */); } - mSyncStorageEngine.setBackoff(account, userId, authority, + mSyncStorageEngine.setBackoff(info, SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); } + /** + * Remove a specified sync, if it exists. + * @param info Authority for which the sync is to be removed. + * @param extras extras bundle to uniquely identify sync. + */ + public void cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras) { + synchronized (mSyncQueue) { + mSyncQueue.remove(info, extras); + } + // Reset the back-off if there are no more syncs pending. + if (!mSyncStorageEngine.isSyncPending(info)) { + mSyncStorageEngine.setBackoff(info, + SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); + } + } + void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) { boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG); if (isLoggable) { Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation); } - operation = new SyncOperation(operation); + operation = new SyncOperation(operation, 0L /* newRunTimeFromNow */); // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given // request. Retries of the request will always honor the backoff, so clear the @@ -1115,25 +1040,29 @@ public class SyncManager { operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF); } - // If this sync aborted because the internal sync loop retried too many times then - // don't reschedule. Otherwise we risk getting into a retry loop. - // If the operation succeeded to some extent then retry immediately. - // If this was a two-way sync then retry soft errors with an exponential backoff. - // If this was an upward sync then schedule a two-way sync immediately. - // Otherwise do not reschedule. if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)) { - Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified " - + operation); + if (isLoggable) { + Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified " + + operation); + } } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false) && !syncResult.syncAlreadyInProgress) { + // If this was an upward sync then schedule a two-way sync immediately. operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD); - Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync " - + "encountered an error: " + operation); + if (isLoggable) { + Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync " + + "encountered an error: " + operation); + } scheduleSyncOperation(operation); } else if (syncResult.tooManyRetries) { - Log.d(TAG, "not retrying sync operation because it retried too many times: " - + operation); + // If this sync aborted because the internal sync loop retried too many times then + // don't reschedule. Otherwise we risk getting into a retry loop. + if (isLoggable) { + Log.d(TAG, "not retrying sync operation because it retried too many times: " + + operation); + } } else if (syncResult.madeSomeProgress()) { + // If the operation succeeded to some extent then retry immediately. if (isLoggable) { Log.d(TAG, "retrying sync operation because even though it had an error " + "it achieved some success"); @@ -1146,19 +1075,18 @@ public class SyncManager { } scheduleSyncOperation( new SyncOperation( - operation.account, operation.userId, - operation.reason, - operation.syncSource, - operation.authority, operation.extras, - DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000, operation.flexTime, - operation.backoff, operation.delayUntil, operation.allowParallelSyncs)); + operation, + DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000 /* newRunTimeFromNow */) + ); } else if (syncResult.hasSoftError()) { + // If this was a two-way sync then retry soft errors with an exponential backoff. if (isLoggable) { Log.d(TAG, "retrying sync operation because it encountered a soft error: " + operation); } scheduleSyncOperation(operation); } else { + // Otherwise do not reschedule. Log.d(TAG, "not retrying sync operation because the error is a hard error: " + operation); } @@ -1191,9 +1119,12 @@ public class SyncManager { updateRunningAccounts(); cancelActiveSync( - null /* any account */, - userId, - null /* any authority */); + new SyncStorageEngine.EndPoint( + null /* any account */, + null /* any authority */, + userId), + null /* any sync. */ + ); } private void onUserRemoved(int userId) { @@ -1202,7 +1133,7 @@ public class SyncManager { // Clean up the storage engine database mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId); synchronized (mSyncQueue) { - mSyncQueue.removeUser(userId); + mSyncQueue.removeUserLocked(userId); } } @@ -1214,6 +1145,7 @@ public class SyncManager { final SyncOperation mSyncOperation; final long mHistoryRowId; ISyncAdapter mSyncAdapter; + ISyncServiceAdapter mSyncServiceAdapter; final long mStartTime; long mTimeoutStartTime; boolean mBound; @@ -1239,10 +1171,10 @@ public class SyncManager { mSyncOperation = syncOperation; mHistoryRowId = historyRowId; mSyncAdapter = null; + mSyncServiceAdapter = null; mStartTime = SystemClock.elapsedRealtime(); mTimeoutStartTime = mStartTime; - mSyncWakeLock = mSyncHandler.getSyncWakeLock( - mSyncOperation.account, mSyncOperation.authority); + mSyncWakeLock = mSyncHandler.getSyncWakeLock(mSyncOperation); mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid)); mSyncWakeLock.acquire(); } @@ -1269,7 +1201,12 @@ public class SyncManager { 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)); + if (mSyncOperation.target.target_provider) { + msg.arg1 = SyncOperation.SYNC_TARGET_ADAPTER; + } else { + msg.arg1 = SyncOperation.SYNC_TARGET_SERVICE; + } + msg.obj = new ServiceConnectionData(this, service); mSyncHandler.sendMessage(msg); } @@ -1280,13 +1217,13 @@ public class SyncManager { mSyncHandler.sendMessage(msg); } - boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) { + boolean bindToSyncAdapter(ComponentName serviceComponent, int userId) { if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this); + Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this); } Intent intent = new Intent(); intent.setAction("android.content.SyncAdapter"); - intent.setComponent(info.componentName); + intent.setComponent(serviceComponent); intent.putExtra(Intent.EXTRA_CLIENT_LABEL, com.android.internal.R.string.sync_binding_label); intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser( @@ -1296,7 +1233,7 @@ public class SyncManager { final boolean bindResult = mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_ALLOW_OOM_MANAGEMENT, - new UserHandle(mSyncOperation.userId)); + new UserHandle(mSyncOperation.target.userId)); if (!bindResult) { mBound = false; } @@ -1319,7 +1256,6 @@ public class SyncManager { mSyncWakeLock.setWorkSource(null); } - @Override public String toString() { StringBuilder sb = new StringBuilder(); toString(sb); @@ -1458,11 +1394,14 @@ public class SyncManager { int row = table.getNumRows(); Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus = mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus( - account.account, account.userId, syncAdapterType.type.authority); + new SyncStorageEngine.EndPoint( + account.account, + syncAdapterType.type.authority, + account.userId)); SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first; SyncStatusInfo status = syncAuthoritySyncStatus.second; - String authority = settings.authority; + String authority = settings.base.provider; if (authority.length() > 50) { authority = authority.substring(authority.length() - 50); } @@ -1583,14 +1522,25 @@ public class SyncManager { int maxAuthority = 0; int maxAccount = 0; for (SyncStorageEngine.SyncHistoryItem item : items) { - SyncStorageEngine.AuthorityInfo authority + SyncStorageEngine.AuthorityInfo authorityInfo = mSyncStorageEngine.getAuthority(item.authorityId); final String authorityName; final String accountKey; - if (authority != null) { - authorityName = authority.authority; - accountKey = authority.account.name + "/" + authority.account.type - + " u" + authority.userId; + if (authorityInfo != null) { + if (authorityInfo.base.target_provider) { + authorityName = authorityInfo.base.provider; + accountKey = authorityInfo.base.account.name + "/" + + authorityInfo.base.account.type + + " u" + authorityInfo.base.userId; + } else if (authorityInfo.base.target_service) { + authorityName = authorityInfo.base.service.getPackageName() + "/" + + authorityInfo.base.service.getClassName() + + " u" + authorityInfo.base.userId; + accountKey = "no account"; + } else { + authorityName = "Unknown"; + accountKey = "Unknown"; + } } else { authorityName = "Unknown"; accountKey = "Unknown"; @@ -1711,14 +1661,25 @@ public class SyncManager { final PackageManager pm = mContext.getPackageManager(); for (int i = 0; i < N; i++) { SyncStorageEngine.SyncHistoryItem item = items.get(i); - SyncStorageEngine.AuthorityInfo authority + SyncStorageEngine.AuthorityInfo authorityInfo = mSyncStorageEngine.getAuthority(item.authorityId); final String authorityName; final String accountKey; - if (authority != null) { - authorityName = authority.authority; - accountKey = authority.account.name + "/" + authority.account.type - + " u" + authority.userId; + if (authorityInfo != null) { + if (authorityInfo.base.target_provider) { + authorityName = authorityInfo.base.provider; + accountKey = authorityInfo.base.account.name + "/" + + authorityInfo.base.account.type + + " u" + authorityInfo.base.userId; + } else if (authorityInfo.base.target_service) { + authorityName = authorityInfo.base.service.getPackageName() + "/" + + authorityInfo.base.service.getClassName() + + " u" + authorityInfo.base.userId; + accountKey = "none"; + } else { + authorityName = "Unknown"; + accountKey = "Unknown"; + } } else { authorityName = "Unknown"; accountKey = "Unknown"; @@ -1777,14 +1738,25 @@ public class SyncManager { if (extras == null || extras.size() == 0) { continue; } - final SyncStorageEngine.AuthorityInfo authority + final SyncStorageEngine.AuthorityInfo authorityInfo = mSyncStorageEngine.getAuthority(item.authorityId); final String authorityName; final String accountKey; - if (authority != null) { - authorityName = authority.authority; - accountKey = authority.account.name + "/" + authority.account.type - + " u" + authority.userId; + if (authorityInfo != null) { + if (authorityInfo.base.target_provider) { + authorityName = authorityInfo.base.provider; + accountKey = authorityInfo.base.account.name + "/" + + authorityInfo.base.account.type + + " u" + authorityInfo.base.userId; + } else if (authorityInfo.base.target_service) { + authorityName = authorityInfo.base.service.getPackageName() + "/" + + authorityInfo.base.service.getClassName() + + " u" + authorityInfo.base.userId; + accountKey = "none"; + } else { + authorityName = "Unknown"; + accountKey = "Unknown"; + } } else { authorityName = "Unknown"; accountKey = "Unknown"; @@ -1928,10 +1900,11 @@ public class SyncManager { class ServiceConnectionData { public final ActiveSyncContext activeSyncContext; - public final ISyncAdapter syncAdapter; - ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) { + public final IBinder adapter; + + ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) { this.activeSyncContext = activeSyncContext; - this.syncAdapter = syncAdapter; + this.adapter = adapter; } } @@ -1951,8 +1924,7 @@ public class SyncManager { public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo(); private Long mAlarmScheduleTime = null; public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker(); - private final HashMap<Pair<Account, String>, PowerManager.WakeLock> mWakeLocks = - Maps.newHashMap(); + private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap(); private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1); @@ -1966,12 +1938,11 @@ public class SyncManager { } } - private PowerManager.WakeLock getSyncWakeLock(Account account, String authority) { - final Pair<Account, String> wakeLockKey = Pair.create(account, authority); + private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) { + final String wakeLockKey = operation.wakeLockKey(); PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey); if (wakeLock == null) { - final String name = SYNC_WAKE_LOCK_PREFIX + "/" + authority + "/" + account.type - + "/" + account.name; + final String name = SYNC_WAKE_LOCK_PREFIX + operation.wakeLockName(); wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); wakeLock.setReferenceCounted(false); mWakeLocks.put(wakeLockKey, wakeLock); @@ -2020,7 +1991,6 @@ public class SyncManager { super(looper); } - @Override public void handleMessage(Message msg) { long earliestFuturePollTime = Long.MAX_VALUE; long nextPendingSyncTime = Long.MAX_VALUE; @@ -2038,12 +2008,13 @@ public class SyncManager { earliestFuturePollTime = scheduleReadyPeriodicSyncs(); switch (msg.what) { case SyncHandler.MESSAGE_CANCEL: { - Pair<Account, String> payload = (Pair<Account, String>) msg.obj; + SyncStorageEngine.EndPoint payload = (SyncStorageEngine.EndPoint) msg.obj; + Bundle extras = msg.peekData(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: " - + payload.first + ", " + payload.second); + + payload + " bundle: " + extras); } - cancelActiveSyncLocked(payload.first, msg.arg1, payload.second); + cancelActiveSyncLocked(payload, extras); nextPendingSyncTime = maybeStartNextSyncLocked(); break; } @@ -2052,35 +2023,39 @@ public class SyncManager { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED"); } - SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload)msg.obj; + SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload) msg.obj; if (!isSyncStillActive(payload.activeSyncContext)) { Log.d(TAG, "handleSyncHandlerMessage: dropping since the " + "sync is no longer active: " + payload.activeSyncContext); break; } - runSyncFinishedOrCanceledLocked(payload.syncResult, payload.activeSyncContext); + runSyncFinishedOrCanceledLocked(payload.syncResult, + payload.activeSyncContext); // since a sync just finished check if it is time to start a new sync nextPendingSyncTime = maybeStartNextSyncLocked(); break; case SyncHandler.MESSAGE_SERVICE_CONNECTED: { - ServiceConnectionData msgData = (ServiceConnectionData)msg.obj; + ServiceConnectionData msgData = (ServiceConnectionData) msg.obj; if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: " + msgData.activeSyncContext); } // check that this isn't an old message if (isSyncStillActive(msgData.activeSyncContext)) { - runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter); + runBoundToAdapter( + msgData.activeSyncContext, + msgData.adapter, + msg.arg1 /* target */); } break; } case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: { final ActiveSyncContext currentSyncContext = - ((ServiceConnectionData)msg.obj).activeSyncContext; + ((ServiceConnectionData) msg.obj).activeSyncContext; if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: " + currentSyncContext); @@ -2089,12 +2064,15 @@ public class SyncManager { if (isSyncStillActive(currentSyncContext)) { // cancel the sync if we have a syncadapter, which means one is // outstanding - if (currentSyncContext.mSyncAdapter != null) { - try { + try { + if (currentSyncContext.mSyncAdapter != null) { currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext); - } catch (RemoteException e) { - // we don't need to retry this in this case + } else if (currentSyncContext.mSyncServiceAdapter != null) { + currentSyncContext.mSyncServiceAdapter + .cancelSync(currentSyncContext); } + } catch (RemoteException e) { + // We don't need to retry this in this case. } // pretend that the sync failed with an IOException, @@ -2139,9 +2117,46 @@ public class SyncManager { } } + private boolean isDispatchable(SyncStorageEngine.EndPoint target) { + final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); + if (target.target_provider) { + // skip the sync if the account of this operation no longer exists + AccountAndUser[] accounts = mRunningAccounts; + if (!containsAccountAndUser( + accounts, target.account, target.userId)) { + return false; + } + if (!mSyncStorageEngine.getMasterSyncAutomatically(target.userId) + || !mSyncStorageEngine.getSyncAutomatically( + target.account, + target.userId, + target.provider)) { + if (isLoggable) { + Log.v(TAG, " Not scheduling periodic operation: sync turned off."); + } + return false; + } + if (getIsSyncable(target.account, target.userId, target.provider) + == 0) { + if (isLoggable) { + Log.v(TAG, " Not scheduling periodic operation: isSyncable == 0."); + } + return false; + } + } else if (target.target_service) { + if (getIsTargetServiceActive(target.service, target.userId) == 0) { + if (isLoggable) { + Log.v(TAG, " Not scheduling periodic operation: isEnabled == 0."); + } + return false; + } + } + return true; + } + /** * Turn any periodic sync operations that are ready to run into pending sync operations. - * @return the desired start time of the earliest future periodic sync operation, + * @return the desired start time of the earliest future periodic sync operation, * in milliseconds since boot */ private long scheduleReadyPeriodicSyncs() { @@ -2149,14 +2164,7 @@ public class SyncManager { if (isLoggable) { Log.v(TAG, "scheduleReadyPeriodicSyncs"); } - final boolean backgroundDataUsageAllowed = - getConnectivityManager().getBackgroundDataSetting(); long earliestFuturePollTime = Long.MAX_VALUE; - if (!backgroundDataUsageAllowed) { - return earliestFuturePollTime; - } - - AccountAndUser[] accounts = mRunningAccounts; final long nowAbsolute = System.currentTimeMillis(); final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis) @@ -2167,23 +2175,8 @@ public class SyncManager { for (Pair<AuthorityInfo, SyncStatusInfo> info : infos) { final AuthorityInfo authorityInfo = info.first; final SyncStatusInfo status = info.second; - // skip the sync if the account of this operation no longer - // exists - if (!containsAccountAndUser( - accounts, authorityInfo.account, authorityInfo.userId)) { - continue; - } - - if (!mSyncStorageEngine.getMasterSyncAutomatically(authorityInfo.userId) - || !mSyncStorageEngine.getSyncAutomatically( - authorityInfo.account, authorityInfo.userId, - authorityInfo.authority)) { - continue; - } - - if (getIsSyncable( - authorityInfo.account, authorityInfo.userId, authorityInfo.authority) - == 0) { + + if (!isDispatchable(authorityInfo.base)) { continue; } @@ -2211,7 +2204,7 @@ public class SyncManager { boolean runEarly = remainingMillis <= flexInMillis && timeSinceLastRunMillis > periodInMillis - flexInMillis; if (isLoggable) { - Log.v(TAG, "sync: " + i + " for " + authorityInfo.authority + "." + Log.v(TAG, "sync: " + i + " for " + authorityInfo.base + "." + " period: " + (periodInMillis) + " flex: " + (flexInMillis) + " remaining: " + (remainingMillis) @@ -2231,41 +2224,49 @@ public class SyncManager { * future, sync now and reinitialize. This can happen for * example if the user changed the time, synced and changed * back. - * Case 3: If we failed to sync at the last scheduled - * time. + * Case 3: If we failed to sync at the last scheduled time. * Case 4: This sync is close enough to the time that we can schedule it. */ - if (runEarly // Case 4 - || remainingMillis == periodInMillis // Case 1 + if (remainingMillis == periodInMillis // Case 1 || lastPollTimeAbsolute > nowAbsolute // Case 2 - || timeSinceLastRunMillis >= periodInMillis) { // Case 3 + || timeSinceLastRunMillis >= periodInMillis // Case 3 + || runEarly) { // Case 4 // Sync now - - final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( - authorityInfo.account, authorityInfo.userId, - authorityInfo.authority); - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey( - authorityInfo.authority, authorityInfo.account.type), - authorityInfo.userId); - if (syncAdapterInfo == null) { - continue; - } + SyncStorageEngine.EndPoint target = authorityInfo.base; + final Pair<Long, Long> backoff = + mSyncStorageEngine.getBackoff(target); mSyncStorageEngine.setPeriodicSyncTime(authorityInfo.ident, authorityInfo.periodicSyncs.get(i), nowAbsolute); - scheduleSyncOperation( - new SyncOperation(authorityInfo.account, authorityInfo.userId, - SyncOperation.REASON_PERIODIC, - SyncStorageEngine.SOURCE_PERIODIC, - authorityInfo.authority, extras, - 0 /* runtime */, 0 /* flex */, - backoff != null ? backoff.first : 0, - mSyncStorageEngine.getDelayUntilTime( - authorityInfo.account, authorityInfo.userId, - authorityInfo.authority), - syncAdapterInfo.type.allowParallelSyncs())); - + + if (target.target_provider) { + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> + syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey( + target.provider, target.account.type), + target.userId); + if (syncAdapterInfo == null) { + continue; + } + scheduleSyncOperation( + new SyncOperation(target.account, target.userId, + SyncOperation.REASON_PERIODIC, + SyncStorageEngine.SOURCE_PERIODIC, + target.provider, extras, + 0 /* runtime */, 0 /* flex */, + backoff != null ? backoff.first : 0, + mSyncStorageEngine.getDelayUntilTime(target), + syncAdapterInfo.type.allowParallelSyncs())); + } else if (target.target_service) { + scheduleSyncOperation( + new SyncOperation(target.service, target.userId, + SyncOperation.REASON_PERIODIC, + SyncStorageEngine.SOURCE_PERIODIC, + extras, + 0 /* runtime */, + 0 /* flex */, + backoff != null ? backoff.first : 0, + mSyncStorageEngine.getDelayUntilTime(target))); + } } // Compute when this periodic sync should next run. long nextPollTimeAbsolute; @@ -2312,8 +2313,7 @@ public 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. - AccountAndUser[] accounts = mRunningAccounts; - if (accounts == INITIAL_ACCOUNTS_ARRAY) { + if (mRunningAccounts == INITIAL_ACCOUNTS_ARRAY) { if (isLoggable) { Log.v(TAG, "maybeStartNextSync: accounts not known, skipping"); } @@ -2323,9 +2323,6 @@ public class SyncManager { // Otherwise consume SyncOperations from the head of the SyncQueue until one is // found that is runnable (not disabled, etc). If that one is ready to run then // start it, otherwise just get out. - final boolean backgroundDataUsageAllowed = - getConnectivityManager().getBackgroundDataSetting(); - final long now = SystemClock.elapsedRealtime(); // will be set to the next time that a sync should be considered for running @@ -2347,40 +2344,23 @@ public class SyncManager { while (operationIterator.hasNext()) { final SyncOperation op = operationIterator.next(); - // Drop the sync if the account of this operation no longer exists. - if (!containsAccountAndUser(accounts, op.account, op.userId)) { - operationIterator.remove(); - mSyncStorageEngine.deleteFromPending(op.pendingOperation); + // If the user is not running, skip the request. + if (!activityManager.isUserRunning(op.target.userId)) { + final UserInfo userInfo = mUserManager.getUserInfo(op.target.userId); + if (userInfo == null) { + removedUsers.add(op.target.userId); + } if (isLoggable) { - Log.v(TAG, " Dropping sync operation: account doesn't exist."); + Log.v(TAG, " Dropping all sync operations for + " + + op.target.userId + ": user not running."); } continue; } - - // Drop this sync request if it isn't syncable. - int syncableState = getIsSyncable( - op.account, op.userId, op.authority); - if (syncableState == 0) { + if (!isOperationValidLocked(op)) { operationIterator.remove(); mSyncStorageEngine.deleteFromPending(op.pendingOperation); - if (isLoggable) { - Log.v(TAG, " Dropping sync operation: isSyncable == 0."); - } continue; } - - // If the user is not running, drop the request. - if (!activityManager.isUserRunning(op.userId)) { - final UserInfo userInfo = mUserManager.getUserInfo(op.userId); - if (userInfo == null) { - removedUsers.add(op.userId); - } - if (isLoggable) { - Log.v(TAG, " Dropping sync operation: user not running."); - } - continue; - } - // If the next run time is in the future, even given the flexible scheduling, // return the time. if (op.effectiveRunTime - op.flexTime > now) { @@ -2388,42 +2368,16 @@ public class SyncManager { nextReadyToRunTime = op.effectiveRunTime; } if (isLoggable) { - Log.v(TAG, " Dropping sync operation: Sync too far in future."); + Log.v(TAG, " Not running sync operation: Sync too far in future." + + "effective: " + op.effectiveRunTime + " flex: " + op.flexTime + + " now: " + now); } continue; } - // TODO: change this behaviour for non-registered syncs. - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(op.authority, op.account.type), op.userId); - - // only proceed if network is connected for requesting UID - final boolean uidNetworkConnected; - if (syncAdapterInfo != null) { - final NetworkInfo networkInfo = getConnectivityManager() - .getActiveNetworkInfoForUid(syncAdapterInfo.uid); - uidNetworkConnected = networkInfo != null && networkInfo.isConnected(); - } else { - uidNetworkConnected = false; - } - - // skip the sync if it isn't manual, and auto sync or - // background data usage is disabled or network is - // disconnected for the target UID. - if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) - && (syncableState > 0) - && (!mSyncStorageEngine.getMasterSyncAutomatically(op.userId) - || !backgroundDataUsageAllowed - || !uidNetworkConnected - || !mSyncStorageEngine.getSyncAutomatically( - op.account, op.userId, op.authority))) { - operationIterator.remove(); - mSyncStorageEngine.deleteFromPending(op.pendingOperation); - continue; - } - + // Add this sync to be run. operations.add(op); } + for (Integer user : removedUsers) { // if it's still removed if (mUserManager.getUserInfo(user) == null) { @@ -2465,13 +2419,9 @@ public class SyncManager { } } } - if (activeOp.account.type.equals(candidate.account.type) - && activeOp.authority.equals(candidate.authority) - && activeOp.userId == candidate.userId - && (!activeOp.allowParallelSyncs - || activeOp.account.name.equals(candidate.account.name))) { + if (activeOp.isConflict(candidate)) { conflict = activeSyncContext; - // don't break out since we want to do a full count of the varieties + // don't break out since we want to do a full count of the varieties. } else { if (candidateIsInitialization == activeOp.isInitialization() && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) { @@ -2521,8 +2471,8 @@ public class SyncManager { // is null. Reschedule the active sync and start the candidate. toReschedule = oldestNonExpeditedRegular; if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to run, " - + oldestNonExpeditedRegular); + Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to" + + " run, " + oldestNonExpeditedRegular); } } else if (longRunning != null && (candidateIsInitialization @@ -2551,7 +2501,113 @@ public class SyncManager { } return nextReadyToRunTime; - } + } + + /** + * Determine if a sync is no longer valid and should be dropped from the sync queue and its + * pending op deleted. + * @param op operation for which the sync is to be scheduled. + */ + private boolean isOperationValidLocked(SyncOperation op) { + final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); + int targetUid; + int state; + final SyncStorageEngine.EndPoint target = op.target; + boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId); + if (target.target_provider) { + // Drop the sync if the account of this operation no longer exists. + AccountAndUser[] accounts = mRunningAccounts; + if (!containsAccountAndUser(accounts, target.account, target.userId)) { + if (isLoggable) { + Log.v(TAG, " Dropping sync operation: account doesn't exist."); + } + return false; + } + // Drop this sync request if it isn't syncable. + state = getIsSyncable(target.account, target.userId, target.provider); + if (state == 0) { + if (isLoggable) { + Log.v(TAG, " Dropping sync operation: isSyncable == 0."); + } + return false; + } + syncEnabled = syncEnabled && mSyncStorageEngine.getSyncAutomatically( + target.account, target.userId, target.provider); + + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; + syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey( + target.provider, target.account.type), target.userId); + if (syncAdapterInfo != null) { + targetUid = syncAdapterInfo.uid; + } else { + if (isLoggable) { + Log.v(TAG, " Dropping sync operation: No sync adapter registered" + + "for: " + target); + } + return false; + } + } else if (target.target_service) { + state = getIsTargetServiceActive(target.service, target.userId); + if (state == 0) { + // TODO: Change this to not drop disabled syncs - keep them in the pending queue. + if (isLoggable) { + Log.v(TAG, " Dropping sync operation: isActive == 0."); + } + return false; + } + try { + targetUid = mContext.getPackageManager() + .getServiceInfo(target.service, 0) + .applicationInfo + .uid; + } catch (PackageManager.NameNotFoundException e) { + if (isLoggable) { + Log.v(TAG, " Dropping sync operation: No service registered for: " + + target.service); + } + return false; + } + } else { + Log.e(TAG, "Unknown target for Sync Op: " + target); + return false; + } + + // We ignore system settings that specify the sync is invalid if: + // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled. + // or + // 2) it's an initialisation sync - we just need to connect to it. + final boolean ignoreSystemConfiguration = + op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) + || (state < 0); + + // Sync not enabled. + if (!syncEnabled && !ignoreSystemConfiguration) { + if (isLoggable) { + Log.v(TAG, " Dropping sync operation: disallowed by settings/network."); + } + return false; + } + // Network down. + final NetworkInfo networkInfo = getConnectivityManager() + .getActiveNetworkInfoForUid(targetUid); + final boolean uidNetworkConnected = networkInfo != null && networkInfo.isConnected(); + if (!uidNetworkConnected && !ignoreSystemConfiguration) { + if (isLoggable) { + Log.v(TAG, " Dropping sync operation: disallowed by settings/network."); + } + return false; + } + // Metered network. + if (op.isNotAllowedOnMetered() && getConnectivityManager().isActiveNetworkMetered() + && !ignoreSystemConfiguration) { + if (isLoggable) { + Log.v(TAG, " Dropping sync operation: not allowed on metered network."); + } + return false; + } + return true; + } private boolean dispatchSyncOperation(SyncOperation op) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -2561,27 +2617,48 @@ public class SyncManager { Log.v(TAG, syncContext.toString()); } } - - // connect to the sync adapter - SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type); - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; - syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId); - if (syncAdapterInfo == null) { - Log.d(TAG, "can't find a sync adapter for " + syncAdapterType - + ", removing settings for it"); - mSyncStorageEngine.removeAuthority(op.account, op.userId, op.authority); - return false; + // Connect to the sync adapter. + int targetUid; + ComponentName targetComponent; + final SyncStorageEngine.EndPoint info = op.target; + if (info.target_provider) { + SyncAdapterType syncAdapterType = + SyncAdapterType.newKey(info.provider, info.account.type); + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; + syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId); + if (syncAdapterInfo == null) { + Log.d(TAG, "can't find a sync adapter for " + syncAdapterType + + ", removing settings for it"); + mSyncStorageEngine.removeAuthority(info); + return false; + } + targetUid = syncAdapterInfo.uid; + targetComponent = syncAdapterInfo.componentName; + } else { + // TODO: Store the uid of the service as part of the authority info in order to + // avoid this call? + try { + targetUid = mContext.getPackageManager() + .getServiceInfo(info.service, 0) + .applicationInfo + .uid; + targetComponent = info.service; + } catch(PackageManager.NameNotFoundException e) { + Log.d(TAG, "Can't find a service for " + info.service + + ", removing settings for it"); + mSyncStorageEngine.removeAuthority(info); + return false; + } } - ActiveSyncContext activeSyncContext = - new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid); + new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid); activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext); mActiveSyncContexts.add(activeSyncContext); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext); } - if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) { - Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo); + if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) { + Log.e(TAG, "Bind attempt failed - target: " + targetComponent); closeActiveSyncContext(activeSyncContext); return false; } @@ -2589,47 +2666,54 @@ public class SyncManager { return true; } - private void runBoundToSyncAdapter(final ActiveSyncContext activeSyncContext, - ISyncAdapter syncAdapter) { - activeSyncContext.mSyncAdapter = syncAdapter; + private void runBoundToAdapter(final ActiveSyncContext activeSyncContext, + IBinder syncAdapter, int target) { final SyncOperation syncOperation = activeSyncContext.mSyncOperation; try { activeSyncContext.mIsLinkedToDeath = true; - syncAdapter.asBinder().linkToDeath(activeSyncContext, 0); - - syncAdapter.startSync(activeSyncContext, syncOperation.authority, - syncOperation.account, syncOperation.extras); + syncAdapter.linkToDeath(activeSyncContext, 0); + + if (syncOperation.target.target_provider) { + activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter); + activeSyncContext.mSyncAdapter + .startSync(activeSyncContext, syncOperation.target.provider, + syncOperation.target.account, syncOperation.extras); + } else if (syncOperation.target.target_service) { + activeSyncContext.mSyncServiceAdapter = + ISyncServiceAdapter.Stub.asInterface(syncAdapter); + activeSyncContext.mSyncServiceAdapter + .startSync(activeSyncContext, syncOperation.extras); + } } catch (RemoteException remoteExc) { Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc); closeActiveSyncContext(activeSyncContext); increaseBackoffSetting(syncOperation); - scheduleSyncOperation(new SyncOperation(syncOperation)); + scheduleSyncOperation( + new SyncOperation(syncOperation, 0L /* newRunTimeFromNow */)); } catch (RuntimeException exc) { closeActiveSyncContext(activeSyncContext); Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc); } } - private void cancelActiveSyncLocked(Account account, int userId, String authority) { + /** + * Cancel the sync for the provided authority that matches the given bundle. Info here + * can have null fields to indicate all the active syncs for that field. + */ + private void cancelActiveSyncLocked(SyncStorageEngine.EndPoint info, Bundle extras) { ArrayList<ActiveSyncContext> activeSyncs = new ArrayList<ActiveSyncContext>(mActiveSyncContexts); for (ActiveSyncContext activeSyncContext : activeSyncs) { if (activeSyncContext != null) { - // if an account was specified then only cancel the sync if it matches - if (account != null) { - if (!account.equals(activeSyncContext.mSyncOperation.account)) { - continue; - } - } - // if an authority was specified then only cancel the sync if it matches - if (authority != null) { - if (!authority.equals(activeSyncContext.mSyncOperation.authority)) { - continue; - } + final SyncStorageEngine.EndPoint opInfo = + activeSyncContext.mSyncOperation.target; + if (!opInfo.matches(info)) { + continue; } - // check if the userid matches - if (userId != UserHandle.USER_ALL - && userId != activeSyncContext.mSyncOperation.userId) { + if (extras != null && + !syncExtrasEquals(activeSyncContext.mSyncOperation.extras, + extras, + false /* no config settings */)) { continue; } runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */, @@ -2642,16 +2726,19 @@ public class SyncManager { ActiveSyncContext activeSyncContext) { boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); + final SyncOperation syncOperation = activeSyncContext.mSyncOperation; + final SyncStorageEngine.EndPoint info = syncOperation.target; + if (activeSyncContext.mIsLinkedToDeath) { - activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); + if (info.target_provider) { + activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); + } else { + activeSyncContext.mSyncServiceAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); + } activeSyncContext.mIsLinkedToDeath = false; } closeActiveSyncContext(activeSyncContext); - - final SyncOperation syncOperation = activeSyncContext.mSyncOperation; - final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime; - String historyMessage; int downstreamActivity; int upstreamActivity; @@ -2693,6 +2780,12 @@ public class SyncManager { } catch (RemoteException e) { // we don't need to retry this in this case } + } else if (activeSyncContext.mSyncServiceAdapter != null) { + try { + activeSyncContext.mSyncServiceAdapter.cancelSync(activeSyncContext); + } catch (RemoteException e) { + // we don't need to retry this in this case + } } historyMessage = SyncStorageEngine.MESG_CANCELED; downstreamActivity = 0; @@ -2702,24 +2795,35 @@ public class SyncManager { stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, upstreamActivity, downstreamActivity, elapsedTime); - if (syncResult != null && syncResult.tooManyDeletions) { - installHandleTooManyDeletesNotification(syncOperation.account, - syncOperation.authority, syncResult.stats.numDeletes, - syncOperation.userId); + // Check for full-resync and schedule it after closing off the last sync. + if (info.target_provider) { + if (syncResult != null && syncResult.tooManyDeletions) { + installHandleTooManyDeletesNotification(info.account, + info.provider, syncResult.stats.numDeletes, + info.userId); + } else { + mNotificationMgr.cancelAsUser(null, + info.account.hashCode() ^ info.provider.hashCode(), + new UserHandle(info.userId)); + } + if (syncResult != null && syncResult.fullSyncRequested) { + scheduleSyncOperation( + new SyncOperation(info.account, info.userId, + syncOperation.reason, + syncOperation.syncSource, info.provider, new Bundle(), + 0 /* delay */, 0 /* flex */, + syncOperation.backoff, syncOperation.delayUntil, + syncOperation.allowParallelSyncs)); + } } else { - mNotificationMgr.cancelAsUser(null, - syncOperation.account.hashCode() ^ syncOperation.authority.hashCode(), - new UserHandle(syncOperation.userId)); - } - - if (syncResult != null && syncResult.fullSyncRequested) { - scheduleSyncOperation( - new SyncOperation(syncOperation.account, syncOperation.userId, - syncOperation.reason, - syncOperation.syncSource, syncOperation.authority, new Bundle(), - 0 /* delay */, 0 /* flex */, - syncOperation.backoff, syncOperation.delayUntil, - syncOperation.allowParallelSyncs)); + if (syncResult != null && syncResult.fullSyncRequested) { + scheduleSyncOperation( + new SyncOperation(info.service, info.userId, + syncOperation.reason, + syncOperation.syncSource, new Bundle(), + 0 /* delay */, 0 /* flex */, + syncOperation.backoff, syncOperation.delayUntil)); + } } // no need to schedule an alarm, as that will be done by our caller. } @@ -2728,7 +2832,7 @@ public class SyncManager { activeSyncContext.close(); mActiveSyncContexts.remove(activeSyncContext); mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, - activeSyncContext.mSyncOperation.userId); + activeSyncContext.mSyncOperation.target.userId); } /** @@ -2991,26 +3095,16 @@ public class SyncManager { } public long insertStartSyncEvent(SyncOperation syncOperation) { - final int source = syncOperation.syncSource; final long now = System.currentTimeMillis(); - - EventLog.writeEvent(2720, syncOperation.authority, - SyncStorageEngine.EVENT_START, source, - syncOperation.account.name.hashCode()); - - return mSyncStorageEngine.insertStartSyncEvent( - syncOperation.account, syncOperation.userId, syncOperation.reason, - syncOperation.authority, - now, source, syncOperation.isInitialization(), syncOperation.extras - ); + EventLog.writeEvent(2720, + syncOperation.toEventLog(SyncStorageEngine.EVENT_START)); + return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now); } public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, int upstreamActivity, int downstreamActivity, long elapsedTime) { - EventLog.writeEvent(2720, syncOperation.authority, - SyncStorageEngine.EVENT_STOP, syncOperation.syncSource, - syncOperation.account.name.hashCode()); - + EventLog.writeEvent(2720, + syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP)); mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, resultMessage, downstreamActivity, upstreamActivity); } @@ -3025,6 +3119,79 @@ public class SyncManager { return false; } + /** + * Sync extra comparison function. + * @param b1 bundle to compare + * @param b2 other bundle to compare + * @param includeSyncSettings if false, ignore system settings in bundle. + */ + public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings) { + if (b1 == b2) { + return true; + } + if (includeSyncSettings && b1.size() != b2.size()) { + return false; + } + for (String key : b1.keySet()) { + if (!includeSyncSettings && isSyncSetting(key)) { + continue; + } + if (!b2.containsKey(key)) { + return false; + } + if (!b1.get(key).equals(b2.get(key))) { + return false; + } + } + return true; + } + + /** + * @return true if the provided key is used by the SyncManager in scheduling the sync. + */ + private static boolean isSyncSetting(String key) { + if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_MANUAL)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_UPLOAD)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_PRIORITY)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED)) { + return true; + } + if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) { + return true; + } + return false; + } + static class PrintTable { private ArrayList<Object[]> mTable = Lists.newArrayList(); private final int mCols; diff --git a/services/java/com/android/server/content/SyncOperation.java b/services/java/com/android/server/content/SyncOperation.java index ce1dde4..eef20b2 100644 --- a/services/java/com/android/server/content/SyncOperation.java +++ b/services/java/com/android/server/content/SyncOperation.java @@ -20,10 +20,9 @@ import android.accounts.Account; import android.content.pm.PackageManager; import android.content.ComponentName; import android.content.ContentResolver; -import android.content.SyncRequest; import android.os.Bundle; import android.os.SystemClock; -import android.util.Pair; +import android.util.Log; /** * Value type that represents a sync operation. @@ -32,10 +31,13 @@ import android.util.Pair; * {@hide} */ public class SyncOperation implements Comparable { + public static final String TAG = "SyncManager"; + public static final int REASON_BACKGROUND_DATA_SETTINGS_CHANGED = -1; public static final int REASON_ACCOUNTS_UPDATED = -2; public static final int REASON_SERVICE_CHANGED = -3; public static final int REASON_PERIODIC = -4; + /** Sync started because it has just been set to isSyncable. */ public static final int REASON_IS_SYNCABLE = -5; /** Sync started because it has just been set to sync automatically. */ public static final int REASON_SYNC_AUTO = -6; @@ -54,19 +56,21 @@ public class SyncOperation implements Comparable { "UserStart", }; - /** Account info to identify a SyncAdapter registered with the system. */ - public final Account account; - /** Authority info to identify a SyncAdapter registered with the system. */ - public final String authority; - /** Service to which this operation will bind to perform the sync. */ - public final ComponentName service; - public final int userId; + public static final int SYNC_TARGET_UNKNOWN = 0; + public static final int SYNC_TARGET_ADAPTER = 1; + public static final int SYNC_TARGET_SERVICE = 2; + + /** Identifying info for the authority for this operation. */ + public final SyncStorageEngine.EndPoint target; + /** Why this sync was kicked off. {@link #REASON_NAMES} */ public final int reason; + /** Where this sync was initiated. */ public int syncSource; public final boolean allowParallelSyncs; public Bundle extras; public final String key; public boolean expedited; + /** Bare-bones version of this operation that is persisted across reboots. */ public SyncStorageEngine.PendingOperation pendingOperation; /** Elapsed real time in millis at which to run this sync. */ public long latestRunTime; @@ -79,25 +83,56 @@ public class SyncOperation implements Comparable { * Depends on max(backoff, latestRunTime, and delayUntil). */ public long effectiveRunTime; - /** Amount of time before {@link effectiveRunTime} from which this sync can run. */ + /** Amount of time before {@link #effectiveRunTime} from which this sync can run. */ public long flexTime; - public SyncOperation(Account account, int userId, int reason, int source, String authority, + public SyncOperation(Account account, int userId, int reason, int source, String provider, Bundle extras, long runTimeFromNow, long flexTime, long backoff, long delayUntil, boolean allowParallelSyncs) { - this.service = null; - this.account = account; - this.authority = authority; - this.userId = userId; + this.target = new SyncStorageEngine.EndPoint(account, provider, userId); this.reason = reason; - this.syncSource = source; this.allowParallelSyncs = allowParallelSyncs; + this.key = initialiseOperation(this.target, source, extras, runTimeFromNow, flexTime, + backoff, delayUntil); + } + + public SyncOperation(ComponentName service, int userId, int reason, int source, + Bundle extras, long runTimeFromNow, long flexTime, long backoff, + long delayUntil) { + this.target = new SyncStorageEngine.EndPoint(service, userId); + // Default to true for sync service. The service itself decides how to handle this. + this.allowParallelSyncs = true; + this.reason = reason; + this.key = + initialiseOperation(this.target, + source, extras, runTimeFromNow, flexTime, backoff, delayUntil); + } + + /** Used to reschedule a sync at a new point in time. */ + SyncOperation(SyncOperation other, long newRunTimeFromNow) { + this.target = other.target; + this.reason = other.reason; + this.expedited = other.expedited; + this.allowParallelSyncs = other.allowParallelSyncs; + // re-use old flex, but only + long newFlexTime = Math.min(other.flexTime, newRunTimeFromNow); + this.key = + initialiseOperation(this.target, + other.syncSource, other.extras, + newRunTimeFromNow /* runTimeFromNow*/, + newFlexTime /* flexTime */, + other.backoff, + 0L /* delayUntil */); + } + + private String initialiseOperation(SyncStorageEngine.EndPoint info, int source, Bundle extras, + long runTimeFromNow, long flexTime, long backoff, long delayUntil) { + this.syncSource = source; this.extras = new Bundle(extras); cleanBundle(this.extras); this.delayUntil = delayUntil; this.backoff = backoff; final long now = SystemClock.elapsedRealtime(); - // Checks the extras bundle. Must occur after we set the internal bundle. if (runTimeFromNow < 0 || isExpedited()) { this.expedited = true; this.latestRunTime = now; @@ -108,41 +143,11 @@ public class SyncOperation implements Comparable { this.flexTime = flexTime; } updateEffectiveRunTime(); - this.key = toKey(); + return toKey(info, extras); } - public SyncOperation(SyncRequest request, int userId, int reason, int source, long backoff, - long delayUntil, boolean allowParallelSyncs) { - if (request.hasAuthority()) { - Pair<Account, String> providerInfo = request.getProviderInfo(); - this.account = providerInfo.first; - this.authority = providerInfo.second; - this.service = null; - } else { - this.service = request.getService(); - this.account = null; - this.authority = null; - } - this.userId = userId; - this.reason = reason; - this.syncSource = source; - this.allowParallelSyncs = allowParallelSyncs; - this.extras = new Bundle(extras); - cleanBundle(this.extras); - this.delayUntil = delayUntil; - this.backoff = backoff; - final long now = SystemClock.elapsedRealtime(); - if (request.isExpedited()) { - this.expedited = true; - this.latestRunTime = now; - this.flexTime = 0; - } else { - this.expedited = false; - this.latestRunTime = now + (request.getSyncRunTime() * 1000); - this.flexTime = request.getSyncFlexTime() * 1000; - } - updateEffectiveRunTime(); - this.key = toKey(); + public boolean matchesAuthority(SyncOperation other) { + return this.target.matches(other.target); } /** @@ -159,11 +164,7 @@ public class SyncOperation implements Comparable { removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS); removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_EXPEDITED); removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS); - removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_ALLOW_METERED); - - // Remove Config data. - bundle.remove(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD); - bundle.remove(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD); + removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISALLOW_METERED); } private void removeFalseExtra(Bundle bundle, String extraName) { @@ -172,22 +173,24 @@ public class SyncOperation implements Comparable { } } - /** Only used to immediately reschedule a sync. */ - SyncOperation(SyncOperation other) { - this.service = other.service; - this.account = other.account; - this.authority = other.authority; - this.userId = other.userId; - this.reason = other.reason; - this.syncSource = other.syncSource; - this.extras = new Bundle(other.extras); - this.expedited = other.expedited; - this.latestRunTime = SystemClock.elapsedRealtime(); - this.flexTime = 0L; - this.backoff = other.backoff; - this.allowParallelSyncs = other.allowParallelSyncs; - this.updateEffectiveRunTime(); - this.key = toKey(); + /** + * Determine whether if this sync operation is running, the provided operation would conflict + * with it. + * Parallel syncs allow multiple accounts to be synced at the same time. + */ + public boolean isConflict(SyncOperation toRun) { + final SyncStorageEngine.EndPoint other = toRun.target; + if (target.target_provider) { + return target.account.type.equals(other.account.type) + && target.provider.equals(other.provider) + && target.userId == other.userId + && (!allowParallelSyncs + || target.account.name.equals(other.account.name)); + } else { + // Ops that target a service default to allow parallel syncs, which is handled by the + // service returning SYNC_IN_PROGRESS if they don't. + return target.service.equals(other.service) && !allowParallelSyncs; + } } @Override @@ -196,18 +199,26 @@ public class SyncOperation implements Comparable { } public String dump(PackageManager pm, boolean useOneLine) { - StringBuilder sb = new StringBuilder() - .append(account.name) + StringBuilder sb = new StringBuilder(); + if (target.target_provider) { + sb.append(target.account.name) .append(" u") - .append(userId).append(" (") - .append(account.type) + .append(target.userId).append(" (") + .append(target.account.type) .append(")") .append(", ") - .append(authority) - .append(", ") - .append(SyncStorageEngine.SOURCES[syncSource]) - .append(", latestRunTime ") - .append(latestRunTime); + .append(target.provider) + .append(", "); + } else if (target.target_service) { + sb.append(target.service.getPackageName()) + .append(" u") + .append(target.userId).append(" (") + .append(target.service.getClassName()).append(")") + .append(", "); + } + sb.append(SyncStorageEngine.SOURCES[syncSource]) + .append(", currentRunTime ") + .append(effectiveRunTime); if (expedited) { sb.append(", EXPEDITED"); } @@ -245,10 +256,6 @@ public class SyncOperation implements Comparable { } } - public boolean isMetered() { - return extras.getBoolean(ContentResolver.SYNC_EXTRAS_ALLOW_METERED, false); - } - public boolean isInitialization() { return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false); } @@ -261,28 +268,39 @@ public class SyncOperation implements Comparable { return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false); } + public boolean isNotAllowedOnMetered() { + return extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, false); + } + /** Changed in V3. */ - private String toKey() { + public static String toKey(SyncStorageEngine.EndPoint info, Bundle extras) { StringBuilder sb = new StringBuilder(); - if (service == null) { - sb.append("authority: ").append(authority); - sb.append(" account {name=" + account.name + ", user=" + userId + ", type=" + account.type + if (info.target_provider) { + sb.append("provider: ").append(info.provider); + sb.append(" account {name=" + info.account.name + + ", user=" + + info.userId + + ", type=" + + info.account.type + "}"); - } else { + } else if (info.target_service) { sb.append("service {package=" ) - .append(service.getPackageName()) + .append(info.service.getPackageName()) .append(" user=") - .append(userId) + .append(info.userId) .append(", class=") - .append(service.getClassName()) + .append(info.service.getClassName()) .append("}"); + } else { + Log.v(TAG, "Converting SyncOperaton to key, invalid target: " + info.toString()); + return ""; } sb.append(" extras: "); extrasToStringBuilder(extras, sb); return sb.toString(); } - public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) { + private static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) { sb.append("["); for (String key : bundle.keySet()) { sb.append(key).append("=").append(bundle.get(key)).append(" "); @@ -290,6 +308,31 @@ public class SyncOperation implements Comparable { sb.append("]"); } + public String wakeLockKey() { + if (target.target_provider) { + return target.account.name + "/" + target.account.type + ":" + target.provider; + } else if (target.target_service) { + return target.service.getPackageName() + "/" + target.service.getClassName(); + } else { + Log.wtf(TAG, "Invalid target getting wakelock for operation - " + key); + return null; + } + } + + public String wakeLockName() { + if (target.target_provider) { + return "/" + target.provider + + "/" + target.account.type + + "/" + target.account.name; + } else if (target.target_service) { + return "/" + target.service.getPackageName() + + "/" + target.service.getClassName(); + } else { + Log.wtf(TAG, "Invalid target getting wakelock name for operation - " + key); + return null; + } + } + /** * Update the effective run time of this Operation based on latestRunTime (specified at * creation time of sync), delayUntil (specified by SyncAdapter), or backoff (specified by @@ -325,4 +368,21 @@ public class SyncOperation implements Comparable { return 0; } } + + // TODO: Test this to make sure that casting to object doesn't lose the type info for EventLog. + public Object[] toEventLog(int event) { + Object[] logArray = new Object[4]; + logArray[1] = event; + logArray[2] = syncSource; + if (target.target_provider) { + logArray[0] = target.provider; + logArray[3] = target.account.name.hashCode(); + } else if (target.target_service) { + logArray[0] = target.service.getPackageName(); + logArray[3] = target.service.hashCode(); + } else { + Log.wtf(TAG, "sync op with invalid target: " + key); + } + return logArray; + } } diff --git a/services/java/com/android/server/content/SyncQueue.java b/services/java/com/android/server/content/SyncQueue.java index 6f3fe6e..da7efba 100644 --- a/services/java/com/android/server/content/SyncQueue.java +++ b/services/java/com/android/server/content/SyncQueue.java @@ -16,12 +16,11 @@ package com.android.server.content; -import android.accounts.Account; import android.content.pm.PackageManager; -import android.content.pm.RegisteredServicesCache; import android.content.SyncAdapterType; import android.content.SyncAdaptersCache; import android.content.pm.RegisteredServicesCache.ServiceInfo; +import android.os.Bundle; import android.os.SystemClock; import android.text.format.DateUtils; import android.util.Log; @@ -60,25 +59,51 @@ public class SyncQueue { public void addPendingOperations(int userId) { for (SyncStorageEngine.PendingOperation op : mSyncStorageEngine.getPendingOperations()) { - if (op.userId != userId) continue; - - final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( - op.account, op.userId, op.authority); - final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(op.authority, op.account.type), op.userId); - if (syncAdapterInfo == null) { - Log.w(TAG, "Missing sync adapter info for authority " + op.authority + ", userId " - + op.userId); - continue; + final SyncStorageEngine.EndPoint info = op.authority; + if (info.userId != userId) continue; + + final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info); + SyncOperation operationToAdd; + if (info.target_provider) { + final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey(info.provider, info.account.type), info.userId); + if (syncAdapterInfo == null) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.w(TAG, "Missing sync adapter info for authority " + op.authority); + } + continue; + } + operationToAdd = new SyncOperation( + info.account, info.userId, op.reason, op.syncSource, info.provider, + op.extras, + 0 /* delay */, + 0 /* flex */, + backoff != null ? backoff.first : 0, + mSyncStorageEngine.getDelayUntilTime(info), + syncAdapterInfo.type.allowParallelSyncs()); + operationToAdd.expedited = op.expedited; + operationToAdd.pendingOperation = op; + add(operationToAdd, op); + } else if (info.target_service) { + try { + mPackageManager.getServiceInfo(info.service, 0); + } catch (PackageManager.NameNotFoundException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.w(TAG, "Missing sync servce for authority " + op.authority); + } + continue; + } + operationToAdd = new SyncOperation( + info.service, info.userId, op.reason, op.syncSource, + op.extras, + 0 /* delay */, + 0 /* flex */, + backoff != null ? backoff.first : 0, + mSyncStorageEngine.getDelayUntilTime(info)); + operationToAdd.expedited = op.expedited; + operationToAdd.pendingOperation = op; + add(operationToAdd, op); } - SyncOperation syncOperation = new SyncOperation( - op.account, op.userId, op.reason, op.syncSource, op.authority, op.extras, - 0 /* delay */, 0 /* flex */, backoff != null ? backoff.first : 0, - mSyncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority), - syncAdapterInfo.type.allowParallelSyncs()); - syncOperation.expedited = op.expedited; - syncOperation.pendingOperation = op; - add(syncOperation, op); } } @@ -119,12 +144,8 @@ public class SyncQueue { operation.pendingOperation = pop; // Don't update the PendingOp if one already exists. This really is just a placeholder, // no actual scheduling info is placed here. - // TODO: Change this to support service components. if (operation.pendingOperation == null) { - pop = new SyncStorageEngine.PendingOperation( - operation.account, operation.userId, operation.reason, operation.syncSource, - operation.authority, operation.extras, operation.expedited); - pop = mSyncStorageEngine.insertIntoPending(pop); + pop = mSyncStorageEngine.insertIntoPending(operation); if (pop == null) { throw new IllegalStateException("error adding pending sync operation " + operation); @@ -136,17 +157,16 @@ public class SyncQueue { return true; } - public void removeUser(int userId) { + public void removeUserLocked(int userId) { ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>(); for (SyncOperation op : mOperationsMap.values()) { - if (op.userId == userId) { + if (op.target.userId == userId) { opsToRemove.add(op); } } - - for (SyncOperation op : opsToRemove) { - remove(op); - } + for (SyncOperation op : opsToRemove) { + remove(op); + } } /** @@ -154,8 +174,15 @@ public class SyncQueue { * @param operation the operation to remove */ public void remove(SyncOperation operation) { + boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG); SyncOperation operationToRemove = mOperationsMap.remove(operation.key); + if (isLoggable) { + Log.d(TAG, "Attempting to remove: " + operation.key); + } if (operationToRemove == null) { + if (isLoggable) { + Log.d(TAG, "Could not find: " + operation.key); + } return; } if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) { @@ -164,41 +191,58 @@ public class SyncQueue { } } - public void onBackoffChanged(Account account, int userId, String providerName, long backoff) { - // for each op that matches the account and provider update its + /** Reset backoffs for all operations in the queue. */ + public void clearBackoffs() { + for (SyncOperation op : mOperationsMap.values()) { + op.backoff = 0L; + op.updateEffectiveRunTime(); + } + } + + public void onBackoffChanged(SyncStorageEngine.EndPoint target, long backoff) { + // For each op that matches the authority of the changed op, update its // backoff and effectiveStartTime for (SyncOperation op : mOperationsMap.values()) { - if (op.account.equals(account) && op.authority.equals(providerName) - && op.userId == userId) { + if (op.target.matches(target)) { op.backoff = backoff; op.updateEffectiveRunTime(); } } } - public void onDelayUntilTimeChanged(Account account, String providerName, long delayUntil) { - // for each op that matches the account and provider update its - // delayUntilTime and effectiveStartTime + public void onDelayUntilTimeChanged(SyncStorageEngine.EndPoint target, long delayUntil) { + // for each op that matches the authority info of the provided op, change the delay time. for (SyncOperation op : mOperationsMap.values()) { - if (op.account.equals(account) && op.authority.equals(providerName)) { + if (op.target.matches(target)) { op.delayUntil = delayUntil; op.updateEffectiveRunTime(); } } } - public void remove(Account account, int userId, String authority) { + /** + * Remove all of the SyncOperations associated with a given target. + * + * @param info target object provided here can have null Account/provider. This is the case + * where you want to remove all ops associated with a provider (null Account) or all ops + * associated with an account (null provider). + * @param extras option bundle to include to further specify which operation to remove. If this + * bundle contains sync settings flags, they are ignored. + */ + public void remove(final SyncStorageEngine.EndPoint info, Bundle extras) { Iterator<Map.Entry<String, SyncOperation>> entries = mOperationsMap.entrySet().iterator(); while (entries.hasNext()) { Map.Entry<String, SyncOperation> entry = entries.next(); SyncOperation syncOperation = entry.getValue(); - if (account != null && !syncOperation.account.equals(account)) { - continue; - } - if (authority != null && !syncOperation.authority.equals(authority)) { + final SyncStorageEngine.EndPoint opInfo = syncOperation.target; + if (!opInfo.matches(info)) { continue; } - if (userId != syncOperation.userId) { + if (extras != null + && !SyncManager.syncExtrasEquals( + syncOperation.extras, + extras, + false /* no config flags*/)) { continue; } entries.remove(); diff --git a/services/java/com/android/server/content/SyncStorageEngine.java b/services/java/com/android/server/content/SyncStorageEngine.java index 1b9ed98..6cc3d8a 100644 --- a/services/java/com/android/server/content/SyncStorageEngine.java +++ b/services/java/com/android/server/content/SyncStorageEngine.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.ISyncStatusObserver; import android.content.PeriodicSync; import android.content.SyncInfo; +import android.content.SyncRequest; import android.content.SyncStatusInfo; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; @@ -36,10 +37,12 @@ import android.os.Message; import android.os.Parcel; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.UserHandle; import android.util.AtomicFile; import android.util.Log; import android.util.Pair; import android.util.SparseArray; +import android.util.ArrayMap; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; @@ -71,7 +74,6 @@ import java.util.TimeZone; public class SyncStorageEngine extends Handler { private static final String TAG = "SyncManager"; - private static final boolean DEBUG = false; private static final String TAG_FILE = "SyncManagerFile"; private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId"; @@ -108,10 +110,7 @@ public class SyncStorageEngine extends Handler { /** Enum value for a local-initiated sync. */ public static final int SOURCE_LOCAL = 1; - /** - * Enum value for a poll-based sync (e.g., upon connection to - * network) - */ + /** Enum value for a poll-based sync (e.g., upon connection to network) */ public static final int SOURCE_POLL = 2; /** Enum value for a user-initiated sync. */ @@ -119,6 +118,9 @@ public class SyncStorageEngine extends Handler { /** Enum value for a periodic sync. */ public static final int SOURCE_PERIODIC = 4; + + /** Enum value for a sync started for a service. */ + public static final int SOURCE_SERVICE = 5; public static final long NOT_IN_BACKOFF_MODE = -1; @@ -128,7 +130,8 @@ public class SyncStorageEngine extends Handler { "LOCAL", "POLL", "USER", - "PERIODIC" }; + "PERIODIC", + "SERVICE"}; // The MESG column will contain one of these or one of the Error types. public static final String MESG_SUCCESS = "success"; @@ -156,41 +159,53 @@ public class SyncStorageEngine extends Handler { } public static class PendingOperation { - final Account account; - final int userId; + final EndPoint authority; final int reason; final int syncSource; - final String authority; final Bundle extras; // note: read-only. - final ComponentName serviceName; final boolean expedited; - int authorityId; + final int authorityId; + // No longer used. + // Keep around for sake up updating from pending.bin to pending.xml byte[] flatExtras; - PendingOperation(Account account, int userId, int reason, int source, - String authority, Bundle extras, boolean expedited) { - this.account = account; - this.userId = userId; + PendingOperation(AuthorityInfo authority, int reason, int source, + Bundle extras, boolean expedited) { + this.authority = authority.base; this.syncSource = source; this.reason = reason; - this.authority = authority; this.extras = extras != null ? new Bundle(extras) : extras; this.expedited = expedited; - this.authorityId = -1; - this.serviceName = null; + this.authorityId = authority.ident; } PendingOperation(PendingOperation other) { - this.account = other.account; - this.userId = other.userId; this.reason = other.reason; this.syncSource = other.syncSource; this.authority = other.authority; this.extras = other.extras; this.authorityId = other.authorityId; this.expedited = other.expedited; - this.serviceName = other.serviceName; + } + + /** + * Considered equal if they target the same sync adapter (A {@link android.content.SyncService} + * is considered an adapter), for the same userId. + * @param other PendingOperation to compare. + * @return true if the two pending ops are the same. + */ + public boolean equals(PendingOperation other) { + return authority.matches(other.authority); + } + + public String toString() { + return "service=" + authority.service + + " user=" + authority.userId + + " auth=" + authority + + " account=" + authority.account + + " src=" + syncSource + + " extras=" + extras; } } @@ -204,17 +219,93 @@ public class SyncStorageEngine extends Handler { } } - public static class AuthorityInfo { + /** Bare bones representation of a sync target. */ + public static class EndPoint { + public final static EndPoint USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL = + new EndPoint(null, null, UserHandle.USER_ALL); final ComponentName service; final Account account; final int userId; - final String authority; + final String provider; + final boolean target_service; + final boolean target_provider; + + public EndPoint(ComponentName service, int userId) { + this.service = service; + this.userId = userId; + this.account = null; + this.provider = null; + this.target_service = true; + this.target_provider = false; + } + + public EndPoint(Account account, String provider, int userId) { + this.account = account; + this.provider = provider; + this.userId = userId; + this.service = null; + this.target_service = false; + this.target_provider = true; + } + + /** + * An Authority for a sync matches if it targets the same sync adapter for the same user. + * If the authority is for a provider/account pair and the account or provider is null, it + * matches by default. + */ + public boolean matches(EndPoint other) { + if (userId != other.userId + && userId != UserHandle.USER_ALL + && other.userId != UserHandle.USER_ALL) { + return false; + } + if (target_service && other.target_service) { + return service.equals(other.service); + } else if (target_provider && other.target_provider) { + boolean accountsMatch; + if (account == null || other.account == null) { + accountsMatch = true; + } else { + accountsMatch = account.equals(other.account); + } + boolean providersMatch; + if (provider == null || other.provider == null) { + providersMatch = true; + } else { + providersMatch = provider.equals(other.provider); + } + return accountsMatch && providersMatch; + } + return false; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + if (target_provider) { + sb.append(account == null ? "ALL ACCS" : account.name) + .append("/") + .append(provider == null ? "ALL PDRS" : provider); + } else if (target_service) { + sb.append(service.getPackageName()); + } else { + sb.append("invalid target"); + } + return sb.toString(); + } + } + + public static class AuthorityInfo { + final EndPoint base; final int ident; boolean enabled; int syncable; + /** Time at which this sync will run, taking into account backoff. */ long backoffTime; + /** Amount of delay due to backoff. */ long backoffDelay; + /** Offset to add to any requests coming to this authority. */ long delayUntil; + final ArrayList<PeriodicSync> periodicSyncs; /** @@ -224,10 +315,7 @@ public class SyncStorageEngine extends Handler { * @param toCopy AuthorityInfo to be copied. */ AuthorityInfo(AuthorityInfo toCopy) { - account = toCopy.account; - userId = toCopy.userId; - authority = toCopy.authority; - service = toCopy.service; + base = toCopy.base; ident = toCopy.ident; enabled = toCopy.enabled; syncable = toCopy.syncable; @@ -241,56 +329,42 @@ public class SyncStorageEngine extends Handler { } } - /** - * Create an authority with one periodic sync scheduled with an empty bundle and syncing - * every day. An empty bundle is considered equal to any other bundle see - * {@link PeriodicSync.syncExtrasEquals}. - * @param account Account that this authority syncs. - * @param userId which user this sync is registered for. - * @param userId user for which this authority is registered. - * @param ident id of this authority. - */ - AuthorityInfo(Account account, int userId, String authority, int ident) { - this.account = account; - this.userId = userId; - this.authority = authority; - this.service = null; - this.ident = ident; - enabled = SYNC_ENABLED_DEFAULT; + AuthorityInfo(EndPoint info, int id) { + base = info; + ident = id; + enabled = info.target_provider ? + SYNC_ENABLED_DEFAULT : true; + // Service is active by default, + if (info.target_service) { + this.syncable = 1; + } + periodicSyncs = new ArrayList<PeriodicSync>(); + defaultInitialisation(); + } + + private void defaultInitialisation() { syncable = -1; // default to "unknown" backoffTime = -1; // if < 0 then we aren't in backoff mode backoffDelay = -1; // if < 0 then we aren't in backoff mode - periodicSyncs = new ArrayList<PeriodicSync>(); - // Old version adds one periodic sync a day. - periodicSyncs.add(new PeriodicSync(account, authority, - new Bundle(), - DEFAULT_POLL_FREQUENCY_SECONDS, - calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS))); + PeriodicSync defaultSync; + // Old version is one sync a day. Empty bundle gets replaced by any addPeriodicSync() + // call. + if (base.target_provider) { + defaultSync = + new PeriodicSync(base.account, base.provider, + new Bundle(), + DEFAULT_POLL_FREQUENCY_SECONDS, + calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS)); + periodicSyncs.add(defaultSync); + } + } /** - * Create an authority with one periodic sync scheduled with an empty bundle and syncing - * every day using a sync service. - * @param cname sync service identifier. - * @param userId user for which this authority is registered. - * @param ident id of this authority. + * Two AuthorityInfos are considered equal if they have the same authority. */ - AuthorityInfo(ComponentName cname, int userId, int ident) { - this.account = null; - this.userId = userId; - this.authority = null; - this.service = cname; - this.ident = ident; - // Sync service is always enabled. - enabled = true; - syncable = -1; // default to "unknown" - backoffTime = -1; // if < 0 then we aren't in backoff mode - backoffDelay = -1; // if < 0 then we aren't in backoff mode - periodicSyncs = new ArrayList<PeriodicSync>(); - periodicSyncs.add(new PeriodicSync(account, authority, - new Bundle(), - DEFAULT_POLL_FREQUENCY_SECONDS, - calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS))); + public boolean equals(EndPoint other) { + return base.matches(other); } } @@ -322,16 +396,9 @@ public class SyncStorageEngine extends Handler { } interface OnSyncRequestListener { - /** - * Called when a sync is needed on an account(s) due to some change in state. - * @param account - * @param userId - * @param reason - * @param authority - * @param extras - */ - public void onSyncRequest(Account account, int userId, int reason, String authority, - Bundle extras); + + /** Called when a sync is needed on an account(s) due to some change in state. */ + public void onSyncRequest(EndPoint info, int reason, Bundle extras); } // Primary list of all syncable authorities. Also our global lock. @@ -357,8 +424,8 @@ public class SyncStorageEngine extends Handler { = new RemoteCallbackList<ISyncStatusObserver>(); /** Reverse mapping for component name -> <userid -> authority id>. */ - private final HashMap<ComponentName, SparseArray<AuthorityInfo>> mServices = - new HashMap<ComponentName, SparseArray<AuthorityInfo>>(); + private final ArrayMap<ComponentName, SparseArray<AuthorityInfo>> mServices = + new ArrayMap<ComponentName, SparseArray<AuthorityInfo>>(); private int mNextAuthorityId = 0; @@ -501,7 +568,7 @@ public class SyncStorageEngine extends Handler { * @return amount of seconds before syncTimeSeconds that the sync can occur. * I.e. * earliest_sync_time = syncTimeSeconds - calculateDefaultFlexTime(syncTimeSeconds) - * The flex time is capped at a percentage of the {@link DEFAULT_POLL_FREQUENCY_SECONDS}. + * The flex time is capped at a percentage of the {@link #DEFAULT_POLL_FREQUENCY_SECONDS}. */ public static long calculateDefaultFlexTime(long syncTimeSeconds) { if (syncTimeSeconds < DEFAULT_MIN_FLEX_ALLOWED_SECS) { @@ -535,7 +602,7 @@ public class SyncStorageEngine extends Handler { mChangeListeners.finishBroadcast(); } - if (DEBUG) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "reportChange " + which + " to: " + reports); } @@ -555,7 +622,8 @@ public class SyncStorageEngine extends Handler { public boolean getSyncAutomatically(Account account, int userId, String providerName) { synchronized (mAuthorities) { if (account != null) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, + AuthorityInfo authority = getAuthorityLocked( + new EndPoint(account, providerName, userId), "getSyncAutomatically"); return authority != null && authority.enabled; } @@ -563,10 +631,9 @@ public class SyncStorageEngine extends Handler { int i = mAuthorities.size(); while (i > 0) { i--; - AuthorityInfo authority = mAuthorities.valueAt(i); - if (authority.authority.equals(providerName) - && authority.userId == userId - && authority.enabled) { + AuthorityInfo authorityInfo = mAuthorities.valueAt(i); + if (authorityInfo.base.matches(new EndPoint(account, providerName, userId)) + && authorityInfo.enabled) { return true; } } @@ -576,15 +643,18 @@ public class SyncStorageEngine extends Handler { public void setSyncAutomatically(Account account, int userId, String providerName, boolean sync) { - if (DEBUG) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName + ", user " + userId + " -> " + sync); } synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1, - false); + AuthorityInfo authority = + getOrCreateAuthorityLocked( + new EndPoint(account, providerName, userId), + -1 /* ident */, + false); if (authority.enabled == sync) { - if (DEBUG) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing"); } return; @@ -603,8 +673,9 @@ public class SyncStorageEngine extends Handler { public int getIsSyncable(Account account, int userId, String providerName) { synchronized (mAuthorities) { if (account != null) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getIsSyncable"); + AuthorityInfo authority = getAuthorityLocked( + new EndPoint(account, providerName, userId), + "get authority syncable"); if (authority == null) { return -1; } @@ -614,9 +685,10 @@ public class SyncStorageEngine extends Handler { int i = mAuthorities.size(); while (i > 0) { i--; - AuthorityInfo authority = mAuthorities.valueAt(i); - if (authority.authority.equals(providerName)) { - return authority.syncable; + AuthorityInfo authorityInfo = mAuthorities.valueAt(i); + if (authorityInfo.base != null + && authorityInfo.base.provider.equals(providerName)) { + return authorityInfo.syncable; } } return -1; @@ -624,103 +696,163 @@ public class SyncStorageEngine extends Handler { } public void setIsSyncable(Account account, int userId, String providerName, int syncable) { + synchronized (mAuthorities) { + AuthorityInfo authority = + getOrCreateAuthorityLocked( + new EndPoint(account, providerName, userId), + -1 /* ident */, + false); + setSyncableLocked(authority, syncable); + } + } + + public int getIsTargetServiceActive(ComponentName cname, int userId) { + synchronized (mAuthorities) { + if (cname != null) { + AuthorityInfo authority = getAuthorityLocked( + new EndPoint(cname, userId), + "get service enabled"); + if (authority == null) { + return -1; + } + return authority.syncable; + } + return -1; + } + } + + public void setIsEnabled(ComponentName cname, int userId, int syncable) { + synchronized (mAuthorities) { + AuthorityInfo authority = + getOrCreateAuthorityLocked( + new EndPoint(cname, userId), -1, false); + setSyncableLocked(authority, syncable); + } + } + + /** + * An enabled sync service and a syncable provider's adapter both get resolved to the same + * persisted variable - namely the "syncable" attribute for an AuthorityInfo in accounts.xml. + * @param aInfo + * @param syncable + */ + private void setSyncableLocked(AuthorityInfo aInfo, int syncable) { if (syncable > 1) { syncable = 1; } else if (syncable < -1) { syncable = -1; } - if (DEBUG) { - Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName - + ", user " + userId + " -> " + syncable); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.d(TAG, "setIsSyncable: " + aInfo.toString() + " -> " + syncable); } - synchronized (mAuthorities) { - AuthorityInfo authority = - getOrCreateAuthorityLocked(account, userId, providerName, -1, false); - if (authority.syncable == syncable) { - if (DEBUG) { - Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing"); - } - return; + + if (aInfo.syncable == syncable) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing"); } - authority.syncable = syncable; - writeAccountInfoLocked(); + return; } + aInfo.syncable = syncable; + writeAccountInfoLocked(); if (syncable > 0) { - requestSync(account, userId, SyncOperation.REASON_IS_SYNCABLE, providerName, - new Bundle()); + requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle()); } reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } - public Pair<Long, Long> getBackoff(Account account, int userId, String providerName) { + public Pair<Long, Long> getBackoff(EndPoint info) { synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getBackoff"); - if (authority == null || authority.backoffTime < 0) { - return null; + AuthorityInfo authority = getAuthorityLocked(info, "getBackoff"); + if (authority != null) { + return Pair.create(authority.backoffTime, authority.backoffDelay); } - return Pair.create(authority.backoffTime, authority.backoffDelay); + return null; } } - public void setBackoff(Account account, int userId, String providerName, - long nextSyncTime, long nextDelay) { - if (DEBUG) { - Log.v(TAG, "setBackoff: " + account + ", provider " + providerName - + ", user " + userId + /** + * Update the backoff for the given endpoint. The endpoint may be for a provider/account and + * the account or provider info be null, which signifies all accounts or providers. + */ + public void setBackoff(EndPoint info, long nextSyncTime, long nextDelay) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "setBackoff: " + info + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay); } - boolean changed = false; + boolean changed; synchronized (mAuthorities) { - if (account == null || providerName == null) { - for (AccountInfo accountInfo : mAccounts.values()) { - if (account != null && !account.equals(accountInfo.accountAndUser.account) - && userId != accountInfo.accountAndUser.userId) { - continue; - } - for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { - if (providerName != null - && !providerName.equals(authorityInfo.authority)) { - continue; - } - if (authorityInfo.backoffTime != nextSyncTime - || authorityInfo.backoffDelay != nextDelay) { - authorityInfo.backoffTime = nextSyncTime; - authorityInfo.backoffDelay = nextDelay; - changed = true; - } - } - } + if (info.target_provider + && (info.account == null || info.provider == null)) { + // Do more work for a provider sync if the provided info has specified all + // accounts/providers. + changed = setBackoffLocked( + info.account /* may be null */, + info.userId, + info.provider /* may be null */, + nextSyncTime, nextDelay); } else { - AuthorityInfo authority = - getOrCreateAuthorityLocked(account, userId, providerName, -1 /* ident */, - true); - if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) { - return; + AuthorityInfo authorityInfo = + getOrCreateAuthorityLocked(info, -1 /* ident */, true); + if (authorityInfo.backoffTime == nextSyncTime + && authorityInfo.backoffDelay == nextDelay) { + changed = false; + } else { + authorityInfo.backoffTime = nextSyncTime; + authorityInfo.backoffDelay = nextDelay; + changed = true; } - authority.backoffTime = nextSyncTime; - authority.backoffDelay = nextDelay; - changed = true; } } - if (changed) { reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } } + /** + * Either set backoff for a specific authority, or set backoff for all the + * accounts on a specific adapter/all adapters. + * + * @param account account for which to set backoff. Null to specify all accounts. + * @param userId id of the user making this request. + * @param providerName provider for which to set backoff. Null to specify all providers. + */ + private boolean setBackoffLocked(Account account, int userId, String providerName, + long nextSyncTime, long nextDelay) { + boolean changed = false; + for (AccountInfo accountInfo : mAccounts.values()) { + if (account != null && !account.equals(accountInfo.accountAndUser.account) + && userId != accountInfo.accountAndUser.userId) { + continue; + } + for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { + if (providerName != null + && !providerName.equals(authorityInfo.base.provider)) { + continue; + } + if (authorityInfo.backoffTime != nextSyncTime + || authorityInfo.backoffDelay != nextDelay) { + authorityInfo.backoffTime = nextSyncTime; + authorityInfo.backoffDelay = nextDelay; + changed = true; + } + } + } + return changed; + } + public void clearAllBackoffs(SyncQueue syncQueue) { boolean changed = false; synchronized (mAuthorities) { synchronized (syncQueue) { + // Clear backoff for all sync adapters. for (AccountInfo accountInfo : mAccounts.values()) { for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) { - if (DEBUG) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "clearAllBackoffs:" - + " authority:" + authorityInfo.authority + + " authority:" + authorityInfo.base + " account:" + accountInfo.accountAndUser.account.name + " user:" + accountInfo.accountAndUser.userId + " backoffTime was: " + authorityInfo.backoffTime @@ -728,12 +860,23 @@ public class SyncStorageEngine extends Handler { } authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE; authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE; - syncQueue.onBackoffChanged(accountInfo.accountAndUser.account, - accountInfo.accountAndUser.userId, authorityInfo.authority, 0); changed = true; } } } + // Clear backoff for all sync services. + for (ComponentName service : mServices.keySet()) { + SparseArray<AuthorityInfo> aInfos = mServices.get(service); + for (int i = 0; i < aInfos.size(); i++) { + AuthorityInfo authorityInfo = aInfos.valueAt(i); + if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE + || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) { + authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE; + authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE; + } + } + } + syncQueue.clearBackoffs(); } } @@ -742,142 +885,158 @@ public class SyncStorageEngine extends Handler { } } - public void setDelayUntilTime(Account account, int userId, String providerName, - long delayUntil) { - if (DEBUG) { - Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName - + ", user " + userId + " -> delayUntil " + delayUntil); - } + public long getDelayUntilTime(EndPoint info) { synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked( - account, userId, providerName, -1 /* ident */, true); - if (authority.delayUntil == delayUntil) { - return; + AuthorityInfo authority = getAuthorityLocked(info, "getDelayUntil"); + if (authority == null) { + return 0; } - authority.delayUntil = delayUntil; + return authority.delayUntil; } - - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } - public long getDelayUntilTime(Account account, int userId, String providerName) { + public void setDelayUntilTime(EndPoint info, long delayUntil) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "setDelayUntil: " + info + + " -> delayUntil " + delayUntil); + } synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getDelayUntil"); - if (authority == null) { - return 0; + AuthorityInfo authority = getOrCreateAuthorityLocked(info, -1, true); + if (authority.delayUntil == delayUntil) { + return; } - return authority.delayUntil; + authority.delayUntil = delayUntil; } + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } - private void updateOrRemovePeriodicSync(PeriodicSync toUpdate, int userId, boolean add) { - if (DEBUG) { - Log.v(TAG, "addOrRemovePeriodicSync: " + toUpdate.account + ", user " + userId - + ", provider " + toUpdate.authority - + " -> period " + toUpdate.period + ", extras " + toUpdate.extras); + public void updateOrAddPeriodicSync(EndPoint info, long period, long flextime, Bundle extras) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "addPeriodicSync: " + info + + " -> period " + period + ", flex " + flextime + ", extras " + + extras.toString()); } synchronized (mAuthorities) { - if (toUpdate.period <= 0 && add) { - Log.e(TAG, "period < 0, should never happen in updateOrRemovePeriodicSync: add-" - + add); + if (period <= 0) { + Log.e(TAG, "period < 0, should never happen in updateOrAddPeriodicSync"); } - if (toUpdate.extras == null) { - Log.e(TAG, "null extras, should never happen in updateOrRemovePeriodicSync: add-" - + add); + if (extras == null) { + Log.e(TAG, "null extras, should never happen in updateOrAddPeriodicSync:"); } try { + PeriodicSync toUpdate; + if (info.target_provider) { + toUpdate = new PeriodicSync(info.account, + info.provider, + extras, + period, + flextime); + } else { + toUpdate = new PeriodicSync(info.service, + extras, + period, + flextime); + } AuthorityInfo authority = - getOrCreateAuthorityLocked(toUpdate.account, userId, toUpdate.authority, - -1, false); - if (add) { - // add this periodic sync if an equivalent periodic doesn't already exist. - boolean alreadyPresent = false; - for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) { - PeriodicSync syncInfo = authority.periodicSyncs.get(i); - if (PeriodicSync.syncExtrasEquals( - toUpdate.extras, - syncInfo.extras)) { - if (toUpdate.period == syncInfo.period && - toUpdate.flexTime == syncInfo.flexTime) { - // Absolutely the same. - return; - } - authority.periodicSyncs.set(i, new PeriodicSync(toUpdate)); - alreadyPresent = true; - break; + getOrCreateAuthorityLocked(info, -1, false); + // add this periodic sync if an equivalent periodic doesn't already exist. + boolean alreadyPresent = false; + for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) { + PeriodicSync syncInfo = authority.periodicSyncs.get(i); + if (SyncManager.syncExtrasEquals(syncInfo.extras, + extras, + true /* includeSyncSettings*/)) { + if (period == syncInfo.period && + flextime == syncInfo.flexTime) { + // Absolutely the same. + Log.e(TAG, "update psync: exactly the same."); + return; } + authority.periodicSyncs.set(i, toUpdate); + alreadyPresent = true; + break; } - // If we added an entry to the periodicSyncs array also add an entry to - // the periodic syncs status to correspond to it. - if (!alreadyPresent) { - authority.periodicSyncs.add(new PeriodicSync(toUpdate)); - SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); - status.setPeriodicSyncTime(authority.periodicSyncs.size() - 1, 0L); - } - } else { - // Remove any periodic syncs that match the authority and extras. - SyncStatusInfo status = mSyncStatus.get(authority.ident); - boolean changed = false; - Iterator<PeriodicSync> iterator = authority.periodicSyncs.iterator(); - int i = 0; - while (iterator.hasNext()) { - PeriodicSync syncInfo = iterator.next(); - if (PeriodicSync.syncExtrasEquals(syncInfo.extras, toUpdate.extras)) { - iterator.remove(); - changed = true; - // If we removed an entry from the periodicSyncs array also - // remove the corresponding entry from the status - if (status != null) { - status.removePeriodicSyncTime(i); - } else { - Log.e(TAG, "Tried removing sync status on remove periodic sync but" - + "did not find it."); - } + } + // If we added an entry to the periodicSyncs array also add an entry to + // the periodic syncs status to correspond to it. + if (!alreadyPresent) { + authority.periodicSyncs.add(toUpdate); + SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); + // A new periodic sync is initialised as already having been run. + status.setPeriodicSyncTime( + authority.periodicSyncs.size() - 1, + System.currentTimeMillis()); + } + } finally { + writeAccountInfoLocked(); + writeStatusLocked(); + } + } + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + } + + public void removePeriodicSync(EndPoint info, Bundle extras) { + synchronized(mAuthorities) { + try { + AuthorityInfo authority = + getOrCreateAuthorityLocked(info, -1, false); + // Remove any periodic syncs that match the authority and extras. + SyncStatusInfo status = mSyncStatus.get(authority.ident); + boolean changed = false; + Iterator<PeriodicSync> iterator = authority.periodicSyncs.iterator(); + int i = 0; + while (iterator.hasNext()) { + PeriodicSync syncInfo = iterator.next(); + if (SyncManager.syncExtrasEquals(syncInfo.extras, + extras, + true /* includeSyncSettings */)) { + iterator.remove(); + changed = true; + // If we removed an entry from the periodicSyncs array also + // remove the corresponding entry from the status + if (status != null) { + status.removePeriodicSyncTime(i); } else { - i++; + Log.e(TAG, "Tried removing sync status on remove periodic sync but" + + " did not find it."); } + } else { + i++; } - if (!changed) { - return; - } + } + if (!changed) { + return; } } finally { writeAccountInfoLocked(); writeStatusLocked(); } } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } - public void addPeriodicSync(PeriodicSync toAdd, int userId) { - updateOrRemovePeriodicSync(toAdd, userId, true /* add */); - } - - public void removePeriodicSync(PeriodicSync toRemove, int userId) { - updateOrRemovePeriodicSync(toRemove, userId, false /* remove */); - } - - public List<PeriodicSync> getPeriodicSyncs(Account account, int userId, String providerName) { - ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>(); + /** + * @return list of periodic syncs for an authority. Never returns null - if no such syncs + * exist, returns an empty list. + */ + public List<PeriodicSync> getPeriodicSyncs(EndPoint info) { synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, - "getPeriodicSyncs"); - if (authority != null) { - for (PeriodicSync item : authority.periodicSyncs) { + AuthorityInfo authorityInfo = getAuthorityLocked(info, "getPeriodicSyncs"); + ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>(); + if (authorityInfo != null) { + for (PeriodicSync item : authorityInfo.periodicSyncs) { // Copy and send out. Necessary for thread-safety although it's parceled. syncs.add(new PeriodicSync(item)); } } + return syncs; } - return syncs; } public void setMasterSyncAutomatically(boolean flag, int userId) { synchronized (mAuthorities) { Boolean auto = mMasterSyncAutomatically.get(userId); - if (auto != null && (boolean) auto == flag) { + if (auto != null && auto == flag) { return; } mMasterSyncAutomatically.put(userId, flag); @@ -898,12 +1057,6 @@ public class SyncStorageEngine extends Handler { } } - public void removeAuthority(Account account, int userId, String authority) { - synchronized (mAuthorities) { - removeAuthorityLocked(account, userId, authority, true /* doWrite */); - } - } - public AuthorityInfo getAuthority(int authorityId) { synchronized (mAuthorities) { return mAuthorities.get(authorityId); @@ -911,53 +1064,47 @@ public class SyncStorageEngine extends Handler { } /** - * Returns true if there is currently a sync operation for the given - * account or authority actively being processed. + * Returns true if there is currently a sync operation being actively processed for the given + * authority. */ - public boolean isSyncActive(Account account, int userId, String authority) { + public boolean isSyncActive(EndPoint info) { synchronized (mAuthorities) { - for (SyncInfo syncInfo : getCurrentSyncs(userId)) { + for (SyncInfo syncInfo : getCurrentSyncs(info.userId)) { AuthorityInfo ainfo = getAuthority(syncInfo.authorityId); - if (ainfo != null && ainfo.account.equals(account) - && ainfo.authority.equals(authority) - && ainfo.userId == userId) { + if (ainfo != null && ainfo.base.matches(info)) { return true; } } } - return false; } - public PendingOperation insertIntoPending(PendingOperation op) { + public PendingOperation insertIntoPending(SyncOperation op) { + PendingOperation pop; synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "insertIntoPending: account=" + op.account - + " user=" + op.userId - + " auth=" + op.authority - + " src=" + op.syncSource + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "insertIntoPending: authority=" + op.target + " extras=" + op.extras); } - - AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, op.userId, - op.authority, - -1 /* desired identifier */, - true /* write accounts to storage */); + final EndPoint info = op.target; + AuthorityInfo authority = + getOrCreateAuthorityLocked(info, + -1 /* desired identifier */, + true /* write accounts to storage */); if (authority == null) { return null; } - op = new PendingOperation(op); - op.authorityId = authority.ident; - mPendingOperations.add(op); - appendPendingOperationLocked(op); + pop = new PendingOperation(authority, op.reason, op.syncSource, op.extras, + op.expedited); + mPendingOperations.add(pop); + writePendingOperationsLocked(); SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); status.pending = true; } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); - return op; + return pop; } /** @@ -965,18 +1112,13 @@ public class SyncStorageEngine extends Handler { * authorities. If there are no more pending syncs for the same authority/account/userid, * update the SyncStatusInfo for that authority(authority here is the internal representation * of a 'sync operation'. - * @param op - * @return + * @param op Pending op to delete. */ public boolean deleteFromPending(PendingOperation op) { boolean res = false; synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "deleteFromPending: account=" + op.account - + " user=" + op.userId - + " auth=" + op.authority - + " src=" + op.syncSource - + " extras=" + op.extras); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "deleteFromPending: account=" + op.toString()); } if (mPendingOperations.remove(op)) { if (mPendingOperations.size() == 0 @@ -986,30 +1128,27 @@ public class SyncStorageEngine extends Handler { } else { mNumPendingFinished++; } - - AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority, - "deleteFromPending"); + AuthorityInfo authority = getAuthorityLocked(op.authority, "deleteFromPending"); if (authority != null) { - if (DEBUG) Log.v(TAG, "removing - " + authority.toString()); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "removing - " + authority.toString()); + } final int N = mPendingOperations.size(); boolean morePending = false; - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { PendingOperation cur = mPendingOperations.get(i); - if (cur.account.equals(op.account) - && cur.authority.equals(op.authority) - && cur.userId == op.userId) { + if (cur.equals(op)) { morePending = true; break; } } if (!morePending) { - if (DEBUG) Log.v(TAG, "no more pending!"); + if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "no more pending!"); SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); status.pending = false; } } - res = true; } } @@ -1044,7 +1183,7 @@ public class SyncStorageEngine extends Handler { */ public void doDatabaseCleanup(Account[] accounts, int userId) { synchronized (mAuthorities) { - if (DEBUG) Log.v(TAG, "Updating for new accounts..."); + if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Updating for new accounts..."); SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>(); Iterator<AccountInfo> accIt = mAccounts.values().iterator(); while (accIt.hasNext()) { @@ -1052,7 +1191,7 @@ public class SyncStorageEngine extends Handler { if (!ArrayUtils.contains(accounts, acc.accountAndUser.account) && acc.accountAndUser.userId == userId) { // This account no longer exists... - if (DEBUG) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Account removed: " + acc.accountAndUser); } for (AuthorityInfo auth : acc.authorities.values()) { @@ -1099,25 +1238,25 @@ public class SyncStorageEngine extends Handler { public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) { final SyncInfo syncInfo; synchronized (mAuthorities) { - if (DEBUG) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "setActiveSync: account=" - + activeSyncContext.mSyncOperation.account - + " auth=" + activeSyncContext.mSyncOperation.authority + + " auth=" + activeSyncContext.mSyncOperation.target + " src=" + activeSyncContext.mSyncOperation.syncSource + " extras=" + activeSyncContext.mSyncOperation.extras); } - AuthorityInfo authority = getOrCreateAuthorityLocked( - activeSyncContext.mSyncOperation.account, - activeSyncContext.mSyncOperation.userId, - activeSyncContext.mSyncOperation.authority, + final EndPoint info = activeSyncContext.mSyncOperation.target; + AuthorityInfo authorityInfo = getOrCreateAuthorityLocked( + info, -1 /* assign a new identifier if creating a new authority */, true /* write to storage if this results in a change */); - syncInfo = new SyncInfo(authority.ident, - authority.account, authority.authority, + syncInfo = new SyncInfo( + authorityInfo.ident, + authorityInfo.base.account, + authorityInfo.base.provider, + authorityInfo.base.service, activeSyncContext.mStartTime); - getCurrentSyncs(authority.userId).add(syncInfo); + getCurrentSyncs(authorityInfo.base.userId).add(syncInfo); } - reportActiveChange(); return syncInfo; } @@ -1127,10 +1266,11 @@ public class SyncStorageEngine extends Handler { */ public void removeActiveSync(SyncInfo syncInfo, int userId) { synchronized (mAuthorities) { - if (DEBUG) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "removeActiveSync: account=" + syncInfo.account + " user=" + userId - + " auth=" + syncInfo.authority); + + " auth=" + syncInfo.authority + + " service=" + syncInfo.service); } getCurrentSyncs(userId).remove(syncInfo); } @@ -1147,36 +1287,37 @@ public class SyncStorageEngine extends Handler { /** * Note that sync has started for the given account and authority. + * + syncOperation.account, syncOperation.userId, syncOperation.reason, + syncOperation.authority, + now, source, syncOperation.isInitialization(), syncOperation.extras */ - public long insertStartSyncEvent(Account accountName, int userId, int reason, - String authorityName, long now, int source, boolean initialization, Bundle extras) { + public long insertStartSyncEvent(SyncOperation op, long now) { long id; synchronized (mAuthorities) { - if (DEBUG) { - Log.v(TAG, "insertStartSyncEvent: account=" + accountName + "user=" + userId - + " auth=" + authorityName + " source=" + source); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "insertStartSyncEvent: " + op); } - AuthorityInfo authority = getAuthorityLocked(accountName, userId, authorityName, - "insertStartSyncEvent"); + AuthorityInfo authority = getAuthorityLocked(op.target, "insertStartSyncEvent"); if (authority == null) { return -1; } SyncHistoryItem item = new SyncHistoryItem(); - item.initialization = initialization; + item.initialization = op.isInitialization(); item.authorityId = authority.ident; item.historyId = mNextHistoryId++; if (mNextHistoryId < 0) mNextHistoryId = 0; item.eventTime = now; - item.source = source; - item.reason = reason; - item.extras = extras; + item.source = op.syncSource; + item.reason = op.reason; + item.extras = op.extras; item.event = EVENT_START; mSyncHistory.add(0, item); while (mSyncHistory.size() > MAX_HISTORY) { mSyncHistory.remove(mSyncHistory.size()-1); } id = item.historyId; - if (DEBUG) Log.v(TAG, "returning historyId " + id); + if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "returning historyId " + id); } reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS); @@ -1186,7 +1327,7 @@ public class SyncStorageEngine extends Handler { public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage, long downstreamActivity, long upstreamActivity) { synchronized (mAuthorities) { - if (DEBUG) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "stopSyncEvent: historyId=" + historyId); } SyncHistoryItem item = null; @@ -1325,10 +1466,9 @@ public class SyncStorageEngine extends Handler { /** * Return a copy of the specified authority with the corresponding sync status */ - public Pair<AuthorityInfo, SyncStatusInfo> getCopyOfAuthorityWithSyncStatus( - Account account, int userId, String authority) { + public Pair<AuthorityInfo, SyncStatusInfo> getCopyOfAuthorityWithSyncStatus(EndPoint info) { synchronized (mAuthorities) { - AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(account, userId, authority, + AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(info, -1 /* assign a new identifier if creating a new authority */, true /* write to storage if this results in a change */); return createCopyPairOfAuthorityWithSyncStatusLocked(authorityInfo); @@ -1352,24 +1492,22 @@ public class SyncStorageEngine extends Handler { /** * Returns the status that matches the authority and account. * - * @param account the account we want to check - * @param authority the authority whose row should be selected - * @return the SyncStatusInfo for the authority + * @param info the target we want to check + * @return the SyncStatusInfo for the target */ - public SyncStatusInfo getStatusByAccountAndAuthority(Account account, int userId, - String authority) { - if (account == null || authority == null) { + public SyncStatusInfo getStatusByAuthority(EndPoint info) { + if (info.target_provider && (info.account == null || info.provider == null)) { throw new IllegalArgumentException(); + } else if (info.target_service && info.service == null) { + throw new IllegalArgumentException(); } synchronized (mAuthorities) { final int N = mSyncStatus.size(); - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { SyncStatusInfo cur = mSyncStatus.valueAt(i); AuthorityInfo ainfo = mAuthorities.get(cur.authorityId); - - if (ainfo != null && ainfo.authority.equals(authority) - && ainfo.userId == userId - && account.equals(ainfo.account)) { + if (ainfo != null + && ainfo.base.matches(info)) { return cur; } } @@ -1377,25 +1515,20 @@ public class SyncStorageEngine extends Handler { } } - /** - * Return true if the pending status is true of any matching authorities. - */ - public boolean isSyncPending(Account account, int userId, String authority) { + /** Return true if the pending status is true of any matching authorities. */ + public boolean isSyncPending(EndPoint info) { synchronized (mAuthorities) { final int N = mSyncStatus.size(); - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { SyncStatusInfo cur = mSyncStatus.valueAt(i); AuthorityInfo ainfo = mAuthorities.get(cur.authorityId); if (ainfo == null) { continue; } - if (userId != ainfo.userId) { + if (!ainfo.base.matches(info)) { continue; } - if (account != null && !ainfo.account.equals(account)) { - continue; - } - if (ainfo.authority.equals(authority) && cur.pending) { + if (cur.pending) { return true; } } @@ -1453,126 +1586,131 @@ public class SyncStorageEngine extends Handler { /** * Retrieve an authority, returning null if one does not exist. * - * @param accountName The name of the account for the authority. - * @param authorityName The name of the authority itself. + * @param info container for the info of the authority to look up. * @param tag If non-null, this will be used in a log message if the * requested authority does not exist. */ - private AuthorityInfo getAuthorityLocked(Account accountName, int userId, String authorityName, - String tag) { - AccountAndUser au = new AccountAndUser(accountName, userId); - AccountInfo accountInfo = mAccounts.get(au); - if (accountInfo == null) { - if (tag != null) { - if (DEBUG) { - Log.v(TAG, tag + ": unknown account " + au); + private AuthorityInfo getAuthorityLocked(EndPoint info, String tag) { + if (info.target_service) { + SparseArray<AuthorityInfo> aInfo = mServices.get(info.service); + AuthorityInfo authority = null; + if (aInfo != null) { + authority = aInfo.get(info.userId); + } + if (authority == null) { + if (tag != null) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, tag + " No authority info found for " + info.service + " for user " + + info.userId); + } } + return null; } - return null; - } - AuthorityInfo authority = accountInfo.authorities.get(authorityName); - if (authority == null) { - if (tag != null) { - if (DEBUG) { - Log.v(TAG, tag + ": unknown authority " + authorityName); + return authority; + } else if (info.target_provider){ + AccountAndUser au = new AccountAndUser(info.account, info.userId); + AccountInfo accountInfo = mAccounts.get(au); + if (accountInfo == null) { + if (tag != null) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, tag + ": unknown account " + au); + } } + return null; } - return null; - } - - return authority; - } - - /** - * Retrieve an authority, returning null if one does not exist. - * - * @param service The service name used for this sync. - * @param userId The user for whom this sync is scheduled. - * @param tag If non-null, this will be used in a log message if the - * requested authority does not exist. - */ - private AuthorityInfo getAuthorityLocked(ComponentName service, int userId, String tag) { - AuthorityInfo authority = mServices.get(service).get(userId); - if (authority == null) { - if (tag != null) { - if (DEBUG) { - Log.v(TAG, tag + " No authority info found for " + service + " for user " - + userId); + AuthorityInfo authority = accountInfo.authorities.get(info.provider); + if (authority == null) { + if (tag != null) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, tag + ": unknown provider " + info.provider); + } } + return null; } + return authority; + } else { + Log.e(TAG, tag + " Authority : " + info + ", invalid target"); return null; } - return authority; } /** - * @param cname identifier for the service. - * @param userId for the syncs corresponding to this authority. + * @param info info identifying authority. * @param ident unique identifier for authority. -1 for none. * @param doWrite if true, update the accounts.xml file on the disk. - * @return the authority that corresponds to the provided sync service, creating it if none + * @return the authority that corresponds to the provided sync authority, creating it if none * exists. */ - private AuthorityInfo getOrCreateAuthorityLocked(ComponentName cname, int userId, int ident, - boolean doWrite) { - SparseArray<AuthorityInfo> aInfo = mServices.get(cname); - if (aInfo == null) { - aInfo = new SparseArray<AuthorityInfo>(); - mServices.put(cname, aInfo); - } - AuthorityInfo authority = aInfo.get(userId); - if (authority == null) { - if (ident < 0) { - ident = mNextAuthorityId; - mNextAuthorityId++; - doWrite = true; - } - if (DEBUG) { - Log.v(TAG, "created a new AuthorityInfo for " + cname.getPackageName() - + ", " + cname.getClassName() - + ", user: " + userId); - } - authority = new AuthorityInfo(cname, userId, ident); - aInfo.put(userId, authority); - mAuthorities.put(ident, authority); - if (doWrite) { - writeAccountInfoLocked(); + private AuthorityInfo getOrCreateAuthorityLocked(EndPoint info, int ident, boolean doWrite) { + AuthorityInfo authority = null; + if (info.target_service) { + SparseArray<AuthorityInfo> aInfo = mServices.get(info.service); + if (aInfo == null) { + aInfo = new SparseArray<AuthorityInfo>(); + mServices.put(info.service, aInfo); + } + authority = aInfo.get(info.userId); + if (authority == null) { + authority = createAuthorityLocked(info, ident, doWrite); + aInfo.put(info.userId, authority); + } + } else if (info.target_provider) { + AccountAndUser au = new AccountAndUser(info.account, info.userId); + AccountInfo account = mAccounts.get(au); + if (account == null) { + account = new AccountInfo(au); + mAccounts.put(au, account); + } + authority = account.authorities.get(info.provider); + if (authority == null) { + authority = createAuthorityLocked(info, ident, doWrite); + account.authorities.put(info.provider, authority); } } return authority; } - private AuthorityInfo getOrCreateAuthorityLocked(Account accountName, int userId, - String authorityName, int ident, boolean doWrite) { - AccountAndUser au = new AccountAndUser(accountName, userId); - AccountInfo account = mAccounts.get(au); - if (account == null) { - account = new AccountInfo(au); - mAccounts.put(au, account); - } - AuthorityInfo authority = account.authorities.get(authorityName); - if (authority == null) { - if (ident < 0) { - ident = mNextAuthorityId; - mNextAuthorityId++; - doWrite = true; - } - if (DEBUG) { - Log.v(TAG, "created a new AuthorityInfo for " + accountName - + ", user " + userId - + ", provider " + authorityName); - } - authority = new AuthorityInfo(accountName, userId, authorityName, ident); - account.authorities.put(authorityName, authority); - mAuthorities.put(ident, authority); - if (doWrite) { - writeAccountInfoLocked(); - } + private AuthorityInfo createAuthorityLocked(EndPoint info, int ident, boolean doWrite) { + AuthorityInfo authority; + if (ident < 0) { + ident = mNextAuthorityId; + mNextAuthorityId++; + doWrite = true; + } + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "created a new AuthorityInfo for " + info); + } + authority = new AuthorityInfo(info, ident); + mAuthorities.put(ident, authority); + if (doWrite) { + writeAccountInfoLocked(); } - return authority; } + public void removeAuthority(EndPoint info) { + synchronized (mAuthorities) { + if (info.target_provider) { + removeAuthorityLocked(info.account, info.userId, info.provider, true /* doWrite */); + } else { + SparseArray<AuthorityInfo> aInfos = mServices.get(info.service); + if (aInfos != null) { + AuthorityInfo authorityInfo = aInfos.get(info.userId); + if (authorityInfo != null) { + mAuthorities.remove(authorityInfo.ident); + aInfos.delete(info.userId); + writeAccountInfoLocked(); + } + } + + } + } + } + + /** + * Remove an authority associated with a provider. Needs to be a standalone function for + * backward compatibility. + */ private void removeAuthorityLocked(Account account, int userId, String authorityName, boolean doWrite) { AccountInfo accountInfo = mAccounts.get(new AccountAndUser(account, userId)); @@ -1591,8 +1729,7 @@ public class SyncStorageEngine extends Handler { * Updates (in a synchronized way) the periodic sync time of the specified * authority id and target periodic sync */ - public void setPeriodicSyncTime( - int authorityId, PeriodicSync targetPeriodicSync, long when) { + public void setPeriodicSyncTime(int authorityId, PeriodicSync targetPeriodicSync, long when) { boolean found = false; final AuthorityInfo authorityInfo; synchronized (mAuthorities) { @@ -1608,7 +1745,7 @@ public class SyncStorageEngine extends Handler { } if (!found) { Log.w(TAG, "Ignoring setPeriodicSyncTime request for a sync that does not exist. " + - "Authority: " + authorityInfo.authority); + "Authority: " + authorityInfo.base); } } @@ -1777,10 +1914,10 @@ public class SyncStorageEngine extends Handler { ArrayList<AuthorityInfo> authoritiesToRemove = new ArrayList<AuthorityInfo>(); final int N = mAuthorities.size(); - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { AuthorityInfo authority = mAuthorities.valueAt(i); // skip this authority if it isn't one of the renamed ones - final String newAuthorityName = sAuthorityRenames.get(authority.authority); + final String newAuthorityName = sAuthorityRenames.get(authority.base); if (newAuthorityName == null) { continue; } @@ -1796,20 +1933,26 @@ public class SyncStorageEngine extends Handler { } // if we already have a record of this new authority then don't copy over the settings - if (getAuthorityLocked(authority.account, authority.userId, newAuthorityName, "cleanup") - != null) { + EndPoint newInfo = + new EndPoint(authority.base.account, + newAuthorityName, + authority.base.userId); + if (getAuthorityLocked(newInfo, "cleanup") != null) { continue; } - AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account, - authority.userId, newAuthorityName, -1 /* ident */, false /* doWrite */); + AuthorityInfo newAuthority = + getOrCreateAuthorityLocked(newInfo, -1 /* ident */, false /* doWrite */); newAuthority.enabled = true; writeNeeded = true; } for (AuthorityInfo authorityInfo : authoritiesToRemove) { - removeAuthorityLocked(authorityInfo.account, authorityInfo.userId, - authorityInfo.authority, false /* doWrite */); + removeAuthorityLocked( + authorityInfo.base.account, + authorityInfo.base.userId, + authorityInfo.base.provider, + false /* doWrite */); writeNeeded = true; } @@ -1865,15 +2008,18 @@ public class SyncStorageEngine extends Handler { } if (authority == null) { if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { - Log.v(TAG, "Creating entry"); + Log.v(TAG_FILE, "Creating authority entry"); } if (accountName != null && accountType != null) { - authority = getOrCreateAuthorityLocked( - new Account(accountName, accountType), userId, authorityName, id, - false); + EndPoint info = + new EndPoint( + new Account(accountName, accountType), + authorityName, userId); + authority = getOrCreateAuthorityLocked(info, id, false); } else { - authority = getOrCreateAuthorityLocked( - new ComponentName(packageName, className), userId, id, false); + EndPoint info = + new EndPoint(new ComponentName(packageName, className), userId); + authority = getOrCreateAuthorityLocked(info, id, false); } // If the version is 0 then we are upgrading from a file format that did not // know about periodic syncs. In that case don't clear the list since we @@ -1905,7 +2051,7 @@ public class SyncStorageEngine extends Handler { /** * Parse a periodic sync from accounts.xml. Sets the bundle to be empty. */ - private PeriodicSync parsePeriodicSync(XmlPullParser parser, AuthorityInfo authority) { + private PeriodicSync parsePeriodicSync(XmlPullParser parser, AuthorityInfo authorityInfo) { Bundle extras = new Bundle(); // Gets filled in later. String periodValue = parser.getAttributeValue(null, "period"); String flexValue = parser.getAttributeValue(null, "flex"); @@ -1930,10 +2076,22 @@ public class SyncStorageEngine extends Handler { Log.d(TAG, "No flex time specified for this sync, using a default. period: " + period + " flex: " + flextime); } - final PeriodicSync periodicSync = - new PeriodicSync(authority.account, authority.authority, extras, + PeriodicSync periodicSync; + if (authorityInfo.base.target_provider) { + periodicSync = + new PeriodicSync(authorityInfo.base.account, + authorityInfo.base.provider, + extras, period, flextime); - authority.periodicSyncs.add(periodicSync); + } else { + periodicSync = + new PeriodicSync( + authorityInfo.base.service, + extras, + period, + flextime); + } + authorityInfo.periodicSyncs.add(periodicSync); return periodicSync; } @@ -2001,17 +2159,18 @@ public class SyncStorageEngine extends Handler { final int N = mAuthorities.size(); for (int i = 0; i < N; i++) { AuthorityInfo authority = mAuthorities.valueAt(i); + EndPoint info = authority.base; out.startTag(null, "authority"); out.attribute(null, "id", Integer.toString(authority.ident)); - out.attribute(null, XML_ATTR_USER, Integer.toString(authority.userId)); + out.attribute(null, XML_ATTR_USER, Integer.toString(info.userId)); out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled)); - if (authority.service == null) { - out.attribute(null, "account", authority.account.name); - out.attribute(null, "type", authority.account.type); - out.attribute(null, "authority", authority.authority); + if (info.service == null) { + out.attribute(null, "account", info.account.name); + out.attribute(null, "type", info.account.type); + out.attribute(null, "authority", info.provider); } else { - out.attribute(null, "package", authority.service.getPackageName()); - out.attribute(null, "class", authority.service.getClassName()); + out.attribute(null, "package", info.service.getPackageName()); + out.attribute(null, "class", info.service.getClassName()); } if (authority.syncable < 0) { out.attribute(null, "syncable", "unknown"); @@ -2105,9 +2264,13 @@ public class SyncStorageEngine extends Handler { accountType = "com.google"; } String authorityName = c.getString(c.getColumnIndex("authority")); - AuthorityInfo authority = this.getOrCreateAuthorityLocked( - new Account(accountName, accountType), 0 /* legacy is single-user */, - authorityName, -1, false); + AuthorityInfo authority = + this.getOrCreateAuthorityLocked( + new EndPoint(new Account(accountName, accountType), + authorityName, + 0 /* legacy is single-user */) + , -1, + false); if (authority != null) { int i = mSyncStatus.size(); boolean found = false; @@ -2159,7 +2322,7 @@ public class SyncStorageEngine extends Handler { while (i > 0) { i--; AuthorityInfo authority = mAuthorities.valueAt(i); - if (authority.authority.equals(provider)) { + if (authority.base.equals(provider)) { authority.enabled = value == null || Boolean.parseBoolean(value); authority.syncable = 1; } @@ -2275,62 +2438,52 @@ public class SyncStorageEngine extends Handler { String tagName = parser.getName(); do { PendingOperation pop = null; - if (eventType == XmlPullParser.START_TAG) { - try { - tagName = parser.getName(); - if (parser.getDepth() == 1 && "op".equals(tagName)) { - // Verify version. - String versionString = - parser.getAttributeValue(null, XML_ATTR_VERSION); - if (versionString == null || - Integer.parseInt(versionString) != PENDING_OPERATION_VERSION) { - Log.w(TAG, "Unknown pending operation version " + versionString); - throw new java.io.IOException("Unknown version."); - } - int authorityId = Integer.valueOf(parser.getAttributeValue( - null, XML_ATTR_AUTHORITYID)); - boolean expedited = Boolean.valueOf(parser.getAttributeValue( - null, XML_ATTR_EXPEDITED)); - int syncSource = Integer.valueOf(parser.getAttributeValue( - null, XML_ATTR_SOURCE)); - int reason = Integer.valueOf(parser.getAttributeValue( - null, XML_ATTR_REASON)); - AuthorityInfo authority = mAuthorities.get(authorityId); - if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { - Log.v(TAG_FILE, authorityId + " " + expedited + " " + syncSource + " " - + reason); - } - if (authority != null) { - pop = new PendingOperation( - authority.account, authority.userId, reason, - syncSource, authority.authority, new Bundle(), - expedited); - pop.flatExtras = null; // No longer used. - mPendingOperations.add(pop); + if (eventType == XmlPullParser.START_TAG) { + try { + tagName = parser.getName(); + if (parser.getDepth() == 2 && "op".equals(tagName)) { + int authorityId = Integer.valueOf(parser.getAttributeValue( + null, XML_ATTR_AUTHORITYID)); + boolean expedited = Boolean.valueOf(parser.getAttributeValue( + null, XML_ATTR_EXPEDITED)); + int syncSource = Integer.valueOf(parser.getAttributeValue( + null, XML_ATTR_SOURCE)); + int reason = Integer.valueOf(parser.getAttributeValue( + null, XML_ATTR_REASON)); + AuthorityInfo authority = mAuthorities.get(authorityId); if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { - Log.v(TAG_FILE, "Adding pending op: " + Log.v(TAG_FILE, authorityId + " " + expedited + " " + + syncSource + " " + reason); + } + if (authority != null) { + pop = new PendingOperation( + authority, reason, syncSource, new Bundle(), expedited); + pop.flatExtras = null; // No longer used. + mPendingOperations.add(pop); + if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { + Log.v(TAG_FILE, "Adding pending op: " + pop.authority + " src=" + pop.syncSource + " reason=" + pop.reason + " expedited=" + pop.expedited); + } + } else { + // Skip non-existent authority. + pop = null; + if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { + Log.v(TAG_FILE, "No authority found for " + authorityId + + ", skipping"); + } } - } else { - // Skip non-existent authority. - pop = null; - if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { - Log.v(TAG_FILE, "No authority found for " + authorityId - + ", skipping"); - } + } else if (parser.getDepth() == 2 && + pop != null && + "extra".equals(tagName)) { + parseExtra(parser, pop.extras); } - } else if (parser.getDepth() == 2 && - pop != null && - "extra".equals(tagName)) { - parseExtra(parser, pop.extras); + } catch (NumberFormatException e) { + Log.d(TAG, "Invalid data in xml file.", e); } - } catch (NumberFormatException e) { - Log.d(TAG, "Invalid data in xml file.", e); } - } eventType = parser.next(); } while(eventType != XmlPullParser.END_DOCUMENT); } catch (java.io.IOException e) { @@ -2348,39 +2501,80 @@ public class SyncStorageEngine extends Handler { } } + static private byte[] flattenBundle(Bundle bundle) { + byte[] flatData = null; + Parcel parcel = Parcel.obtain(); + try { + bundle.writeToParcel(parcel, 0); + flatData = parcel.marshall(); + } finally { + parcel.recycle(); + } + return flatData; + } + + static private Bundle unflattenBundle(byte[] flatData) { + Bundle bundle; + Parcel parcel = Parcel.obtain(); + try { + parcel.unmarshall(flatData, 0, flatData.length); + parcel.setDataPosition(0); + bundle = parcel.readBundle(); + } catch (RuntimeException e) { + // A RuntimeException is thrown if we were unable to parse the parcel. + // Create an empty parcel in this case. + bundle = new Bundle(); + } finally { + parcel.recycle(); + } + return bundle; + } + private static final String XML_ATTR_AUTHORITYID = "authority_id"; private static final String XML_ATTR_SOURCE = "source"; private static final String XML_ATTR_EXPEDITED = "expedited"; private static final String XML_ATTR_REASON = "reason"; - private static final String XML_ATTR_VERSION = "version"; /** * Write all currently pending ops to the pending ops file. + * TODO: Change this from xml so that we can append to this file as before. */ private void writePendingOperationsLocked() { final int N = mPendingOperations.size(); FileOutputStream fos = null; try { if (N == 0) { - if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { - Log.v(TAG_FILE, "Truncating " + mPendingFile.getBaseFile()); + if (Log.isLoggable(TAG_FILE, Log.VERBOSE)){ + Log.v(TAG, "Truncating " + mPendingFile.getBaseFile()); } mPendingFile.truncate(); return; } if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { - Log.v(TAG_FILE, "Writing new " + mPendingFile.getBaseFile()); + Log.v(TAG, "Writing new " + mPendingFile.getBaseFile()); } fos = mPendingFile.startWrite(); XmlSerializer out = new FastXmlSerializer(); out.setOutput(fos, "utf-8"); + out.startDocument(null, true); + out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + out.startTag(null, "pending"); + out.attribute(null, "version", Integer.toString(PENDING_OPERATION_VERSION)); for (int i = 0; i < N; i++) { PendingOperation pop = mPendingOperations.get(i); - writePendingOperationLocked(pop, out); - } - out.endDocument(); - mPendingFile.finishWrite(fos); + out.startTag(null, "op"); + out.attribute(null, XML_ATTR_AUTHORITYID, Integer.toString(pop.authorityId)); + out.attribute(null, XML_ATTR_SOURCE, Integer.toString(pop.syncSource)); + out.attribute(null, XML_ATTR_EXPEDITED, Boolean.toString(pop.expedited)); + out.attribute(null, XML_ATTR_REASON, Integer.toString(pop.reason)); + extrasToXml(out, pop.extras); + out.endTag(null, "op"); + } + out.endTag(null, "pending"); + out.endDocument(); + mPendingFile.finishWrite(fos); } catch (java.io.IOException e1) { Log.w(TAG, "Error writing pending operations", e1); if (fos != null) { @@ -2389,85 +2583,6 @@ public class SyncStorageEngine extends Handler { } } - /** Write all currently pending ops to the pending ops file. */ - private void writePendingOperationLocked(PendingOperation pop, XmlSerializer out) - throws IOException { - // Pending operation. - out.startTag(null, "op"); - - out.attribute(null, XML_ATTR_VERSION, Integer.toString(PENDING_OPERATION_VERSION)); - out.attribute(null, XML_ATTR_AUTHORITYID, Integer.toString(pop.authorityId)); - out.attribute(null, XML_ATTR_SOURCE, Integer.toString(pop.syncSource)); - out.attribute(null, XML_ATTR_EXPEDITED, Boolean.toString(pop.expedited)); - out.attribute(null, XML_ATTR_REASON, Integer.toString(pop.reason)); - extrasToXml(out, pop.extras); - - out.endTag(null, "op"); - } - - /** - * Append the given operation to the pending ops file; if unable to, - * write all pending ops. - */ - private void appendPendingOperationLocked(PendingOperation op) { - if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { - Log.v(TAG, "Appending to " + mPendingFile.getBaseFile()); - } - FileOutputStream fos = null; - try { - fos = mPendingFile.openAppend(); - } catch (java.io.IOException e) { - if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) { - Log.v(TAG, "Failed append; writing full file"); - } - writePendingOperationsLocked(); - return; - } - - try { - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(fos, "utf-8"); - writePendingOperationLocked(op, out); - out.endDocument(); - mPendingFile.finishWrite(fos); - } catch (java.io.IOException e1) { - Log.w(TAG, "Error writing appending operation", e1); - mPendingFile.failWrite(fos); - } finally { - try { - fos.close(); - } catch (IOException e) {} - } - } - - static private byte[] flattenBundle(Bundle bundle) { - byte[] flatData = null; - Parcel parcel = Parcel.obtain(); - try { - bundle.writeToParcel(parcel, 0); - flatData = parcel.marshall(); - } finally { - parcel.recycle(); - } - return flatData; - } - - static private Bundle unflattenBundle(byte[] flatData) { - Bundle bundle; - Parcel parcel = Parcel.obtain(); - try { - parcel.unmarshall(flatData, 0, flatData.length); - parcel.setDataPosition(0); - bundle = parcel.readBundle(); - } catch (RuntimeException e) { - // A RuntimeException is thrown if we were unable to parse the parcel. - // Create an empty parcel in this case. - bundle = new Bundle(); - } finally { - parcel.recycle(); - } - return bundle; - } private void extrasToXml(XmlSerializer out, Bundle extras) throws java.io.IOException { for (String key : extras.keySet()) { @@ -2501,6 +2616,26 @@ public class SyncStorageEngine extends Handler { } } + private void requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras) { + if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID + && mSyncRequestListener != null) { + mSyncRequestListener.onSyncRequest(authorityInfo.base, reason, extras); + } else { + SyncRequest.Builder req = + new SyncRequest.Builder() + .syncOnce(0, 0) + .setExtras(extras); + if (authorityInfo.base.target_provider) { + req.setSyncAdapter( + authorityInfo.base.account, + authorityInfo.base.provider); + } else { + req.setSyncAdapter(authorityInfo.base.service); + } + ContentResolver.requestSync(req.build()); + } + } + private void requestSync(Account account, int userId, int reason, String authority, Bundle extras) { // If this is happening in the system process, then call the syncrequest listener @@ -2509,7 +2644,10 @@ public class SyncStorageEngine extends Handler { // which will know which userId to apply based on the Binder id. if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID && mSyncRequestListener != null) { - mSyncRequestListener.onSyncRequest(account, userId, reason, authority, extras); + mSyncRequestListener.onSyncRequest( + new EndPoint(account, authority, userId), + reason, + extras); } else { ContentResolver.requestSync(account, authority, extras); } @@ -2605,10 +2743,8 @@ public class SyncStorageEngine extends Handler { public void dumpPendingOperations(StringBuilder sb) { sb.append("Pending Ops: ").append(mPendingOperations.size()).append(" operation(s)\n"); for (PendingOperation pop : mPendingOperations) { - sb.append("(" + pop.account) - .append(", u" + pop.userId) - .append(", " + pop.authority) - .append(", " + pop.extras) + sb.append("(info: " + pop.authority.toString()) + .append(", extras: " + pop.extras) .append(")\n"); } } diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java index e44652f..853d2a2 100644 --- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java +++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java @@ -17,6 +17,7 @@ package com.android.server.content; import android.accounts.Account; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; @@ -32,22 +33,33 @@ import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; +import com.android.server.content.SyncStorageEngine.EndPoint; + import com.android.internal.os.AtomicFile; import java.io.File; import java.io.FileOutputStream; import java.util.List; +import com.android.server.content.SyncStorageEngine.EndPoint; + public class SyncStorageEngineTest extends AndroidTestCase { protected Account account1; + protected ComponentName syncService1; protected String authority1 = "testprovider"; protected Bundle defaultBundle; protected final int DEFAULT_USER = 0; - + + /* Some default poll frequencies. */ + final long dayPoll = (60 * 60 * 24); + final long dayFuzz = 60; + final long thousandSecs = 1000; + final long thousandSecsFuzz = 100; + MockContentResolver mockResolver; SyncStorageEngine engine; - + private File getSyncDir() { return new File(new File(getContext().getFilesDir(), "system"), "sync"); } @@ -55,6 +67,7 @@ public class SyncStorageEngineTest extends AndroidTestCase { @Override public void setUp() { account1 = new Account("a@example.com", "example.type"); + syncService1 = new ComponentName("com.example", "SyncService"); // Default bundle. defaultBundle = new Bundle(); defaultBundle.putInt("int_key", 0); @@ -80,11 +93,13 @@ public class SyncStorageEngineTest extends AndroidTestCase { SyncStorageEngine engine = SyncStorageEngine.newTestInstance( new TestContext(mockResolver, getContext())); - long time0 = 1000; - long historyId = engine.insertStartSyncEvent( - account, 0, SyncOperation.REASON_PERIODIC, authority, time0, - SyncStorageEngine.SOURCE_LOCAL, false /* initialization */, null /* extras */); + SyncOperation op = new SyncOperation(account, 0, + SyncOperation.REASON_PERIODIC, + SyncStorageEngine.SOURCE_LOCAL, + authority, + null, time0, 0 /* flex*/, 0, 0, true); + long historyId = engine.insertStartSyncEvent(op, time0); long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2; engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0); } @@ -94,27 +109,28 @@ public class SyncStorageEngineTest extends AndroidTestCase { */ @MediumTest public void testPending() throws Exception { - SyncStorageEngine.PendingOperation pop = - new SyncStorageEngine.PendingOperation(account1, DEFAULT_USER, - SyncOperation.REASON_PERIODIC, SyncStorageEngine.SOURCE_LOCAL, - authority1, defaultBundle, false); - - engine.insertIntoPending(pop); + SyncOperation sop = new SyncOperation(account1, + DEFAULT_USER, + SyncOperation.REASON_PERIODIC, + SyncStorageEngine.SOURCE_LOCAL, authority1, null, + 0 /* runtime */, 0 /* flex */, 0 /* backoff */, 0 /* delayuntil */, + true /* expedited */); + engine.insertIntoPending(sop); + // Force engine to read from disk. engine.clearAndReadState(); assert(engine.getPendingOperationCount() == 1); List<SyncStorageEngine.PendingOperation> pops = engine.getPendingOperations(); SyncStorageEngine.PendingOperation popRetrieved = pops.get(0); - assertEquals(pop.account, popRetrieved.account); - assertEquals(pop.reason, popRetrieved.reason); - assertEquals(pop.userId, popRetrieved.userId); - assertEquals(pop.syncSource, popRetrieved.syncSource); - assertEquals(pop.authority, popRetrieved.authority); - assertEquals(pop.expedited, popRetrieved.expedited); - assertEquals(pop.serviceName, popRetrieved.serviceName); - assert(android.content.PeriodicSync.syncExtrasEquals(pop.extras, popRetrieved.extras)); - + assertEquals(sop.target.account, popRetrieved.authority.account); + assertEquals(sop.target.provider, popRetrieved.authority.provider); + assertEquals(sop.target.service, popRetrieved.authority.service); + assertEquals(sop.target.userId, popRetrieved.authority.userId); + assertEquals(sop.reason, popRetrieved.reason); + assertEquals(sop.syncSource, popRetrieved.syncSource); + assertEquals(sop.expedited, popRetrieved.expedited); + assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras)); } /** @@ -134,42 +150,44 @@ public class SyncStorageEngineTest extends AndroidTestCase { final int period2 = 1000; PeriodicSync sync1 = new PeriodicSync(account1, authority, extras1, period1); + EndPoint end1 = new EndPoint(account1, authority, 0); + PeriodicSync sync2 = new PeriodicSync(account1, authority, extras2, period1); PeriodicSync sync3 = new PeriodicSync(account1, authority, extras2, period2); PeriodicSync sync4 = new PeriodicSync(account2, authority, extras2, period2); - + removePeriodicSyncs(engine, account1, 0, authority); removePeriodicSyncs(engine, account2, 0, authority); removePeriodicSyncs(engine, account1, 1, authority); // this should add two distinct periodic syncs for account1 and one for account2 - engine.addPeriodicSync(sync1, 0); - engine.addPeriodicSync(sync2, 0); - engine.addPeriodicSync(sync3, 0); - engine.addPeriodicSync(sync4, 0); + engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 0), period1, 0, extras1); + engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 0), period1, 0, extras2); + engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 0), period2, 0, extras2); + engine.updateOrAddPeriodicSync(new EndPoint(account2, authority, 0), period2, 0, extras2); // add a second user - engine.addPeriodicSync(sync2, 1); + engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 1), period1, 0, extras2); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, 0, authority); + List<PeriodicSync> syncs = engine.getPeriodicSyncs(new EndPoint(account1, authority, 0)); assertEquals(2, syncs.size()); assertEquals(sync1, syncs.get(0)); assertEquals(sync3, syncs.get(1)); - engine.removePeriodicSync(sync1, 0); + engine.removePeriodicSync(new EndPoint(account1, authority, 0), extras1); - syncs = engine.getPeriodicSyncs(account1, 0, authority); + syncs = engine.getPeriodicSyncs(new EndPoint(account1, authority, 0)); assertEquals(1, syncs.size()); assertEquals(sync3, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account2, 0, authority); + syncs = engine.getPeriodicSyncs(new EndPoint(account2, authority, 0)); assertEquals(1, syncs.size()); assertEquals(sync4, syncs.get(0)); - syncs = engine.getPeriodicSyncs(sync2.account, 1, sync2.authority); + syncs = engine.getPeriodicSyncs(new EndPoint(sync2.account, sync2.authority, 1)); assertEquals(1, syncs.size()); assertEquals(sync2, syncs.get(0)); } @@ -190,12 +208,19 @@ public class SyncStorageEngineTest extends AndroidTestCase { final int period2 = 1000; final int flex1 = 10; final int flex2 = 100; + EndPoint point1 = new EndPoint(account1, authority, 0); + EndPoint point2 = new EndPoint(account2, authority, 0); + EndPoint point1User2 = new EndPoint(account1, authority, 1); PeriodicSync sync1 = new PeriodicSync(account1, authority, extras1, period1, flex1); PeriodicSync sync2 = new PeriodicSync(account1, authority, extras2, period1, flex1); PeriodicSync sync3 = new PeriodicSync(account1, authority, extras2, period2, flex2); PeriodicSync sync4 = new PeriodicSync(account2, authority, extras2, period2, flex2); + EndPoint target1 = new EndPoint(account1, authority, 0); + EndPoint target2 = new EndPoint(account2, authority, 0); + EndPoint target1UserB = new EndPoint(account1, authority, 1); + MockContentResolver mockResolver = new MockContentResolver(); SyncStorageEngine engine = SyncStorageEngine.newTestInstance( @@ -206,40 +231,42 @@ public class SyncStorageEngineTest extends AndroidTestCase { removePeriodicSyncs(engine, account1, 1, authority); // This should add two distinct periodic syncs for account1 and one for account2 - engine.addPeriodicSync(sync1, 0); - engine.addPeriodicSync(sync2, 0); - engine.addPeriodicSync(sync3, 0); // Should edit sync2 and update the period. - engine.addPeriodicSync(sync4, 0); - // add a second user - engine.addPeriodicSync(sync2, 1); + engine.updateOrAddPeriodicSync(target1, period1, flex1, extras1); + engine.updateOrAddPeriodicSync(target1, period1, flex1, extras2); + // Edit existing sync and update the period and flex. + engine.updateOrAddPeriodicSync(target1, period2, flex2, extras2); + engine.updateOrAddPeriodicSync(target2, period2, flex2, extras2); + // add a target for a second user. + engine.updateOrAddPeriodicSync(target1UserB, period1, flex1, extras2); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, 0, authority); + List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1); assertEquals(2, syncs.size()); assertEquals(sync1, syncs.get(0)); assertEquals(sync3, syncs.get(1)); - engine.removePeriodicSync(sync1, 0); + engine.removePeriodicSync(target1, extras1); - syncs = engine.getPeriodicSyncs(account1, 0, authority); + syncs = engine.getPeriodicSyncs(target1); assertEquals(1, syncs.size()); assertEquals(sync3, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account2, 0, authority); + syncs = engine.getPeriodicSyncs(target2); assertEquals(1, syncs.size()); assertEquals(sync4, syncs.get(0)); - syncs = engine.getPeriodicSyncs(sync2.account, 1, sync2.authority); + syncs = engine.getPeriodicSyncs(target1UserB); assertEquals(1, syncs.size()); assertEquals(sync2, syncs.get(0)); } private void removePeriodicSyncs(SyncStorageEngine engine, Account account, int userId, String authority) { - engine.setIsSyncable(account, userId, authority, engine.getIsSyncable(account, 0, authority)); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, userId, authority); + EndPoint target = new EndPoint(account, authority, userId); + engine.setIsSyncable(account, userId, authority, engine.getIsSyncable(account, userId, authority)); + List<PeriodicSync> syncs = engine.getPeriodicSyncs(target); for (PeriodicSync sync : syncs) { - engine.removePeriodicSync(sync, userId); + engine.removePeriodicSync(target, sync.extras); } } @@ -264,16 +291,19 @@ public class SyncStorageEngineTest extends AndroidTestCase { final int flex1 = 10; final int flex2 = 100; + EndPoint point1 = new EndPoint(account1, authority1, 0); + EndPoint point2 = new EndPoint(account1, authority2, 0); + EndPoint point3 = new EndPoint(account2, authority1, 0); + PeriodicSync sync1 = new PeriodicSync(account1, authority1, extras1, period1, flex1); PeriodicSync sync2 = new PeriodicSync(account1, authority1, extras2, period1, flex1); PeriodicSync sync3 = new PeriodicSync(account1, authority2, extras1, period1, flex1); PeriodicSync sync4 = new PeriodicSync(account1, authority2, extras2, period2, flex2); PeriodicSync sync5 = new PeriodicSync(account2, authority1, extras1, period1, flex1); - MockContentResolver mockResolver = new MockContentResolver(); - - SyncStorageEngine engine = SyncStorageEngine.newTestInstance( - new TestContext(mockResolver, getContext())); + EndPoint target1 = new EndPoint(account1, authority1, 0); + EndPoint target2 = new EndPoint(account1, authority2, 0); + EndPoint target3 = new EndPoint(account2, authority1, 0); removePeriodicSyncs(engine, account1, 0, authority1); removePeriodicSyncs(engine, account2, 0, authority1); @@ -294,26 +324,26 @@ public class SyncStorageEngineTest extends AndroidTestCase { engine.setIsSyncable(account2, 0, authority2, 0); engine.setSyncAutomatically(account2, 0, authority2, true); - engine.addPeriodicSync(sync1, 0); - engine.addPeriodicSync(sync2, 0); - engine.addPeriodicSync(sync3, 0); - engine.addPeriodicSync(sync4, 0); - engine.addPeriodicSync(sync5, 0); + engine.updateOrAddPeriodicSync(target1, period1, flex1, extras1); + engine.updateOrAddPeriodicSync(target1, period1, flex1, extras2); + engine.updateOrAddPeriodicSync(target2, period1, flex1, extras1); + engine.updateOrAddPeriodicSync(target2, period2, flex2, extras2); + engine.updateOrAddPeriodicSync(target3, period1, flex1, extras1); engine.writeAllState(); engine.clearAndReadState(); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account1, 0, authority1); + List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1); assertEquals(2, syncs.size()); assertEquals(sync1, syncs.get(0)); assertEquals(sync2, syncs.get(1)); - syncs = engine.getPeriodicSyncs(account1, 0, authority2); + syncs = engine.getPeriodicSyncs(target2); assertEquals(2, syncs.size()); assertEquals(sync3, syncs.get(0)); assertEquals(sync4, syncs.get(1)); - syncs = engine.getPeriodicSyncs(account2, 0, authority1); + syncs = engine.getPeriodicSyncs(target3); assertEquals(1, syncs.size()); assertEquals(sync5, syncs.get(0)); @@ -328,6 +358,35 @@ public class SyncStorageEngineTest extends AndroidTestCase { assertEquals(0, engine.getIsSyncable(account2, 0, authority2)); } + @SmallTest + public void testComponentParsing() throws Exception { + // Sync Service component. + PeriodicSync sync1 = new PeriodicSync(syncService1, Bundle.EMPTY, dayPoll, dayFuzz); + + byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<accounts version=\"2\" >\n" + + "<authority id=\"0\" user=\"0\" package=\"" + syncService1.getPackageName() + "\"" + + " class=\"" + syncService1.getClassName() + "\" syncable=\"true\">" + + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>" + + "\n</authority>" + + "</accounts>").getBytes(); + + File syncDir = getSyncDir(); + syncDir.mkdirs(); + AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); + FileOutputStream fos = accountInfoFile.startWrite(); + fos.write(accountsFileData); + accountInfoFile.finishWrite(fos); + + engine.clearAndReadState(); + + // Test service component read + List<PeriodicSync> syncs = engine.getPeriodicSyncs( + new SyncStorageEngine.EndPoint(syncService1, 0)); + assertEquals(1, syncs.size()); + assertEquals(1, engine.getIsTargetServiceActive(syncService1, 0)); + } + @MediumTest /** * V2 introduces flex time as well as service components. @@ -339,20 +398,20 @@ public class SyncStorageEngineTest extends AndroidTestCase { final String authority2 = "auth2"; final String authority3 = "auth3"; - final long dayPoll = (60 * 60 * 24); - final long dayFuzz = 60; - final long thousandSecs = 1000; - final long thousandSecsFuzz = 100; - final Bundle extras = new Bundle(); - PeriodicSync sync1 = new PeriodicSync(account, authority1, extras, dayPoll, dayFuzz); - PeriodicSync sync2 = new PeriodicSync(account, authority2, extras, dayPoll, dayFuzz); - PeriodicSync sync3 = new PeriodicSync(account, authority3, extras, dayPoll, dayFuzz); - PeriodicSync sync1s = new PeriodicSync(account, authority1, extras, thousandSecs, thousandSecsFuzz); - PeriodicSync sync2s = new PeriodicSync(account, authority2, extras, thousandSecs, thousandSecsFuzz); - PeriodicSync sync3s = new PeriodicSync(account, authority3, extras, thousandSecs, thousandSecsFuzz); - MockContentResolver mockResolver = new MockContentResolver(); - - final TestContext testContext = new TestContext(mockResolver, getContext()); + EndPoint target1 = new EndPoint(account, authority1, 0); + EndPoint target2 = new EndPoint(account, authority2, 0); + EndPoint target3 = new EndPoint(account, authority3, 0); + EndPoint target4 = new EndPoint(account, authority3, 1); + + PeriodicSync sync1 = new PeriodicSync(account, authority1, Bundle.EMPTY, dayPoll, dayFuzz); + PeriodicSync sync2 = new PeriodicSync(account, authority2, Bundle.EMPTY, dayPoll, dayFuzz); + PeriodicSync sync3 = new PeriodicSync(account, authority3, Bundle.EMPTY, dayPoll, dayFuzz); + PeriodicSync sync1s = new PeriodicSync(account, authority1, Bundle.EMPTY, thousandSecs, + thousandSecsFuzz); + PeriodicSync sync2s = new PeriodicSync(account, authority2, Bundle.EMPTY, thousandSecs, + thousandSecsFuzz); + PeriodicSync sync3s = new PeriodicSync(account, authority3, Bundle.EMPTY, thousandSecs, + thousandSecsFuzz); byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<accounts version=\"2\" >\n" @@ -378,21 +437,22 @@ public class SyncStorageEngineTest extends AndroidTestCase { fos.write(accountsFileData); accountInfoFile.finishWrite(fos); - SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); + engine.clearAndReadState(); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, 0, authority1); + List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1); assertEquals("Got incorrect # of syncs", 1, syncs.size()); assertEquals(sync1, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, 0, authority2); + syncs = engine.getPeriodicSyncs(target2); assertEquals(1, syncs.size()); assertEquals(sync2, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, 0, authority3); + syncs = engine.getPeriodicSyncs(target3); assertEquals(1, syncs.size()); assertEquals(sync3, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, 1, authority3); + syncs = engine.getPeriodicSyncs(target4); + assertEquals(1, syncs.size()); assertEquals(sync3, syncs.get(0)); @@ -411,13 +471,13 @@ public class SyncStorageEngineTest extends AndroidTestCase { engine.clearAndReadState(); - syncs = engine.getPeriodicSyncs(account, 0, authority1); + syncs = engine.getPeriodicSyncs(target1); assertEquals(0, syncs.size()); - syncs = engine.getPeriodicSyncs(account, 0, authority2); + syncs = engine.getPeriodicSyncs(target2); assertEquals(0, syncs.size()); - syncs = engine.getPeriodicSyncs(account, 0, authority3); + syncs = engine.getPeriodicSyncs(target3); assertEquals(0, syncs.size()); accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" @@ -440,15 +500,15 @@ public class SyncStorageEngineTest extends AndroidTestCase { engine.clearAndReadState(); - syncs = engine.getPeriodicSyncs(account, 0, authority1); + syncs = engine.getPeriodicSyncs(target1); assertEquals(1, syncs.size()); assertEquals(sync1s, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, 0, authority2); + syncs = engine.getPeriodicSyncs(target2); assertEquals(1, syncs.size()); assertEquals(sync2s, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, 0, authority3); + syncs = engine.getPeriodicSyncs(target3); assertEquals(1, syncs.size()); assertEquals(sync3s, syncs.get(0)); } @@ -460,6 +520,12 @@ public class SyncStorageEngineTest extends AndroidTestCase { final String authority2 = "auth2"; final String authority3 = "auth3"; final Bundle extras = new Bundle(); + + EndPoint target1 = new EndPoint(account, authority1, 0); + EndPoint target2 = new EndPoint(account, authority2, 0); + EndPoint target3 = new EndPoint(account, authority3, 0); + EndPoint target4 = new EndPoint(account, authority3, 1); + PeriodicSync sync1 = new PeriodicSync(account, authority1, extras, (long) (60 * 60 * 24)); PeriodicSync sync2 = new PeriodicSync(account, authority2, extras, (long) (60 * 60 * 24)); PeriodicSync sync3 = new PeriodicSync(account, authority3, extras, (long) (60 * 60 * 24)); @@ -488,19 +554,20 @@ public class SyncStorageEngineTest extends AndroidTestCase { SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); - List<PeriodicSync> syncs = engine.getPeriodicSyncs(account, 0, authority1); + List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1); assertEquals(1, syncs.size()); assertEquals("expected sync1: " + sync1.toString() + " == sync 2" + syncs.get(0).toString(), sync1, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, 0, authority2); + syncs = engine.getPeriodicSyncs(target2); assertEquals(1, syncs.size()); assertEquals(sync2, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, 0, authority3); + syncs = engine.getPeriodicSyncs(target3); assertEquals(1, syncs.size()); assertEquals(sync3, syncs.get(0)); + syncs = engine.getPeriodicSyncs(target4); + - syncs = engine.getPeriodicSyncs(account, 1, authority3); assertEquals(1, syncs.size()); assertEquals(sync3, syncs.get(0)); @@ -518,13 +585,13 @@ public class SyncStorageEngineTest extends AndroidTestCase { engine.clearAndReadState(); - syncs = engine.getPeriodicSyncs(account, 0, authority1); + syncs = engine.getPeriodicSyncs(target1); assertEquals(0, syncs.size()); - syncs = engine.getPeriodicSyncs(account, 0, authority2); + syncs = engine.getPeriodicSyncs(target2); assertEquals(0, syncs.size()); - syncs = engine.getPeriodicSyncs(account, 0, authority3); + syncs = engine.getPeriodicSyncs(target3); assertEquals(0, syncs.size()); accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" @@ -547,15 +614,15 @@ public class SyncStorageEngineTest extends AndroidTestCase { engine.clearAndReadState(); - syncs = engine.getPeriodicSyncs(account, 0, authority1); + syncs = engine.getPeriodicSyncs(target1); assertEquals(1, syncs.size()); assertEquals(sync1s, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, 0, authority2); + syncs = engine.getPeriodicSyncs(target2); assertEquals(1, syncs.size()); assertEquals(sync2s, syncs.get(0)); - syncs = engine.getPeriodicSyncs(account, 0, authority3); + syncs = engine.getPeriodicSyncs(target3); assertEquals(1, syncs.size()); assertEquals(sync3s, syncs.get(0)); } |