diff --git a/Android.mk b/Android.mk index 125de6876aaf6..22ee3a69f14e8 100644 --- a/Android.mk +++ b/Android.mk @@ -105,9 +105,9 @@ LOCAL_SRC_FILES += \ core/java/android/content/IIntentReceiver.aidl \ core/java/android/content/IIntentSender.aidl \ core/java/android/content/IOnPrimaryClipChangedListener.aidl \ + core/java/android/content/IAnonymousSyncAdapter.aidl \ core/java/android/content/ISyncAdapter.aidl \ core/java/android/content/ISyncContext.aidl \ - core/java/android/content/ISyncServiceAdapter.aidl \ core/java/android/content/ISyncStatusObserver.aidl \ core/java/android/content/pm/IPackageDataObserver.aidl \ core/java/android/content/pm/IPackageDeleteObserver.aidl \ diff --git a/api/current.txt b/api/current.txt index 7805422f2887d..2e7e452f8b0ef 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5583,7 +5583,6 @@ package android.content { method public final android.os.Bundle call(android.net.Uri, java.lang.String, java.lang.String, android.os.Bundle); method public deprecated void cancelSync(android.net.Uri); method public static void cancelSync(android.accounts.Account, java.lang.String); - method public static void cancelSync(android.content.ComponentName); method public static void cancelSync(android.content.SyncRequest); method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); method public static deprecated android.content.SyncInfo getCurrentSync(); @@ -5593,17 +5592,13 @@ package android.content { method public static boolean getMasterSyncAutomatically(); method public android.net.Uri[] getOutgoingUriPermissionGrants(int, int); method public static java.util.List getPeriodicSyncs(android.accounts.Account, java.lang.String); - method public static java.util.List getPeriodicSyncs(android.content.ComponentName); method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String); method public static android.content.SyncAdapterType[] getSyncAdapterTypes(); method public static boolean getSyncAutomatically(android.accounts.Account, java.lang.String); method public final java.lang.String getType(android.net.Uri); method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues); - method public static boolean isServiceActive(android.content.ComponentName); method public static boolean isSyncActive(android.accounts.Account, java.lang.String); - method public static boolean isSyncActive(android.content.ComponentName); method public static boolean isSyncPending(android.accounts.Account, java.lang.String); - method public static boolean isSyncPending(android.content.ComponentName); method public void notifyChange(android.net.Uri, android.database.ContentObserver); method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean); method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException; @@ -5621,7 +5616,6 @@ package android.content { method public static void requestSync(android.content.SyncRequest); method public static void setIsSyncable(android.accounts.Account, java.lang.String, int); method public static void setMasterSyncAutomatically(boolean); - method public static void setServiceActive(android.content.ComponentName, boolean); method public static void setSyncAutomatically(android.accounts.Account, java.lang.String, boolean); method public deprecated void startSync(android.net.Uri, android.os.Bundle); method public final void unregisterContentObserver(android.database.ContentObserver); @@ -6683,7 +6677,6 @@ package android.content { method public void writeToParcel(android.os.Parcel, int); field public final android.accounts.Account account; field public final java.lang.String authority; - field public final android.content.ComponentName service; field public final long startTime; } @@ -6739,7 +6732,6 @@ package android.content { ctor public SyncService(); method public android.os.IBinder onBind(android.content.Intent); method public abstract void onPerformSync(android.os.Bundle, android.content.SyncResult); - method protected boolean parallelSyncsEnabled(); } public class SyncStats implements android.os.Parcelable { diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index b2f27dcc8a25d..45fed2defce56 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -142,7 +142,7 @@ public abstract class ContentResolver { public static final String SYNC_EXTRAS_PRIORITY = "sync_priority"; /** {@hide} Flag to allow sync to occur on metered network. */ - public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered"; + public static final String SYNC_EXTRAS_ALLOW_METERED = "allow_metered"; /** * Set by the SyncManager to request that the SyncAdapter initialize itself for @@ -1571,24 +1571,11 @@ public abstract class ContentResolver { */ public static void cancelSync(Account account, String authority) { try { - getContentService().cancelSync(account, authority, null); + getContentService().cancelSync(account, authority); } catch (RemoteException e) { } } - /** - * Cancel any active or pending syncs that are running on this service. - * - * @param cname the service for which to cancel all active/pending operations. - */ - public static void cancelSync(ComponentName cname) { - try { - getContentService().cancelSync(null, null, cname); - } catch (RemoteException e) { - - } - } - /** * Get information about the SyncAdapters that are known to the system. * @return an array of SyncAdapters that have registered with the system @@ -1655,10 +1642,6 @@ public abstract class ContentResolver { * *

This method requires the caller to hold the permission * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}. - *

The bundle for a periodic sync can be queried by applications with the correct - * permissions using - * {@link ContentResolver#getPeriodicSyncs(Account account, String provider)}, so no - * sensitive data should be transferred here. * * @param account the account to specify in the sync * @param authority the provider to specify in the sync request @@ -1676,7 +1659,13 @@ public abstract class ContentResolver { if (authority == null) { throw new IllegalArgumentException("authority must not be null"); } - if (invalidPeriodicExtras(extras)) { + if (extras.getBoolean(SYNC_EXTRAS_MANUAL, false) + || extras.getBoolean(SYNC_EXTRAS_DO_NOT_RETRY, false) + || extras.getBoolean(SYNC_EXTRAS_IGNORE_BACKOFF, false) + || extras.getBoolean(SYNC_EXTRAS_IGNORE_SETTINGS, false) + || extras.getBoolean(SYNC_EXTRAS_INITIALIZE, false) + || extras.getBoolean(SYNC_EXTRAS_FORCE, false) + || extras.getBoolean(SYNC_EXTRAS_EXPEDITED, false)) { throw new IllegalArgumentException("illegal extras were set"); } try { @@ -1687,26 +1676,6 @@ public abstract class ContentResolver { } } - /** - * {@hide} - * Helper function to throw an IllegalArgumentException if any illegal - * extras were set for a periodic sync. - * - * @param extras bundle to validate. - */ - public static boolean invalidPeriodicExtras(Bundle extras) { - if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false) - || extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false) - || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false) - || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) - || extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false) - || extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false) - || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) { - return true; - } - return false; - } - /** * Remove a periodic sync. Has no affect if account, authority and extras don't match * an existing periodic sync. @@ -1733,28 +1702,19 @@ public abstract class ContentResolver { } /** - * Remove the specified sync. This will cancel any pending or active syncs. If the request is - * for a periodic sync, this call will remove any future occurrences. - *

If a periodic sync is specified, the caller must hold the permission - * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}. If this SyncRequest targets a - * SyncService adapter,the calling application must be signed with the same certificate as the - * adapter. - *

It is possible to cancel a sync using a SyncRequest object that is not the same object - * with which you requested the sync. Do so by building a SyncRequest with the same + * Remove the specified sync. This will remove any syncs that have been scheduled to run, but + * will not cancel any running syncs. + *

This method requires the caller to hold the permission

+ * If the request is for a periodic sync this will cancel future occurrences of the sync. + * + * It is possible to cancel a sync using a SyncRequest object that is different from the object + * with which you requested the sync. Do so by building a SyncRequest with exactly the same * service/adapter, frequency, and extras bundle. * * @param request SyncRequest object containing information about sync to cancel. */ public static void cancelSync(SyncRequest request) { - if (request == null) { - throw new IllegalArgumentException("request cannot be null"); - } - try { - getContentService().cancelRequest(request); - } catch (RemoteException e) { - // exception ignored; if this is thrown then it means the runtime is in the midst of - // being restarted - } + // TODO: Finish this implementation. } /** @@ -1774,23 +1734,7 @@ public abstract class ContentResolver { throw new IllegalArgumentException("authority must not be null"); } try { - return getContentService().getPeriodicSyncs(account, authority, null); - } catch (RemoteException e) { - throw new RuntimeException("the ContentService should always be reachable", e); - } - } - - /** - * Return periodic syncs associated with the provided component. - *

The calling application must be signed with the same certificate as the target component, - * otherwise this call will fail. - */ - public static List getPeriodicSyncs(ComponentName cname) { - if (cname == null) { - throw new IllegalArgumentException("Component must not be null"); - } - try { - return getContentService().getPeriodicSyncs(null, null, cname); + return getContentService().getPeriodicSyncs(account, authority); } catch (RemoteException e) { throw new RuntimeException("the ContentService should always be reachable", e); } @@ -1825,38 +1769,6 @@ public abstract class ContentResolver { } } - /** - * Set whether the provided {@link SyncService} is available to process work. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}. - *

The calling application must be signed with the same certificate as the target component, - * otherwise this call will fail. - */ - public static void setServiceActive(ComponentName cname, boolean active) { - try { - getContentService().setServiceActive(cname, active); - } catch (RemoteException e) { - // exception ignored; if this is thrown then it means the runtime is in the midst of - // being restarted - } - } - - /** - * Query the state of this sync service. - *

Set with {@link #setServiceActive(ComponentName cname, boolean active)}. - *

The calling application must be signed with the same certificate as the target component, - * otherwise this call will fail. - * @param cname ComponentName referring to a {@link SyncService} - * @return true if jobs will be run on this service, false otherwise. - */ - public static boolean isServiceActive(ComponentName cname) { - try { - return getContentService().isServiceActive(cname); - } catch (RemoteException e) { - throw new RuntimeException("the ContentService should always be reachable", e); - } - } - /** * Gets the master auto-sync setting that applies to all the providers and accounts. * If this is false then the per-provider auto-sync setting is ignored. @@ -1891,8 +1803,8 @@ public abstract class ContentResolver { } /** - * 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 for the given + * account or authority in the pending list, or actively being processed. *

This method requires the caller to hold the permission * {@link android.Manifest.permission#READ_SYNC_STATS}. * @param account the account whose setting we are querying @@ -1900,26 +1812,8 @@ public abstract class ContentResolver { * @return true if a sync is active for the given account or authority. */ public static boolean isSyncActive(Account account, String authority) { - if (account == null) { - throw new IllegalArgumentException("account must not be null"); - } - if (authority == null) { - throw new IllegalArgumentException("authority must not be null"); - } - try { - return getContentService().isSyncActive(account, authority, null); - } catch (RemoteException e) { - throw new RuntimeException("the ContentService should always be reachable", e); - } - } - - public static boolean isSyncActive(ComponentName cname) { - if (cname == null) { - throw new IllegalArgumentException("component name must not be null"); - } - try { - return getContentService().isSyncActive(null, null, cname); + return getContentService().isSyncActive(account, authority); } catch (RemoteException e) { throw new RuntimeException("the ContentService should always be reachable", e); } @@ -1977,7 +1871,7 @@ public abstract class ContentResolver { */ public static SyncStatusInfo getSyncStatus(Account account, String authority) { try { - return getContentService().getSyncStatus(account, authority, null); + return getContentService().getSyncStatus(account, authority); } catch (RemoteException e) { throw new RuntimeException("the ContentService should always be reachable", e); } @@ -1993,15 +1887,7 @@ public abstract class ContentResolver { */ public static boolean isSyncPending(Account account, String authority) { try { - return getContentService().isSyncPending(account, authority, null); - } catch (RemoteException e) { - throw new RuntimeException("the ContentService should always be reachable", e); - } - } - - public static boolean isSyncPending(ComponentName cname) { - try { - return getContentService().isSyncPending(null, null, cname); + return getContentService().isSyncPending(account, authority); } catch (RemoteException e) { throw new RuntimeException("the ContentService should always be reachable", e); } diff --git a/core/java/android/content/ISyncServiceAdapter.aidl b/core/java/android/content/IAnonymousSyncAdapter.aidl similarity index 97% rename from core/java/android/content/ISyncServiceAdapter.aidl rename to core/java/android/content/IAnonymousSyncAdapter.aidl index d419307e7fa24..a80cea3d19df5 100644 --- a/core/java/android/content/ISyncServiceAdapter.aidl +++ b/core/java/android/content/IAnonymousSyncAdapter.aidl @@ -24,7 +24,7 @@ import android.content.ISyncContext; * Provider specified). See {@link android.content.AbstractThreadedSyncAdapter}. * {@hide} */ -oneway interface ISyncServiceAdapter { +oneway interface IAnonymousSyncAdapter { /** * Initiate a sync. SyncAdapter-specific parameters may be specified in diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl index 73a76e8051270..9ad5a19b2e4a2 100644 --- a/core/java/android/content/IContentService.aidl +++ b/core/java/android/content/IContentService.aidl @@ -17,7 +17,6 @@ package android.content; import android.accounts.Account; -import android.content.ComponentName; import android.content.SyncInfo; import android.content.ISyncStatusObserver; import android.content.SyncAdapterType; @@ -56,14 +55,8 @@ interface IContentService { int userHandle); void requestSync(in Account account, String authority, in Bundle extras); - /** - * Start a sync given a request. - */ void sync(in SyncRequest request); - void cancelSync(in Account account, String authority, in ComponentName cname); - - /** Cancel a sync, providing information about the sync to be cancelled. */ - void cancelRequest(in SyncRequest request); + void cancelSync(in Account account, String authority); /** * Check if the provider should be synced when a network tickle is received @@ -81,14 +74,12 @@ interface IContentService { void setSyncAutomatically(in Account account, String providerName, boolean sync); /** - * Get a list of periodic operations for a specified authority, or service. - * @param account account for authority, must be null if cname is non-null. - * @param providerName name of provider, must be null if cname is non-null. - * @param cname component to identify sync service, must be null if account/providerName are - * non-null. + * Get the frequency of the periodic poll, if any. + * @param providerName the provider whose setting we are querying + * @return the frequency of the periodic sync in seconds. If 0 then no periodic syncs + * will take place. */ - List getPeriodicSyncs(in Account account, String providerName, - in ComponentName cname); + List getPeriodicSyncs(in Account account, String providerName); /** * Set whether or not the provider is to be synced on a periodic basis. @@ -121,23 +112,16 @@ interface IContentService { */ void setIsSyncable(in Account account, String providerName, int syncable); - /** - * Corresponds roughly to setIsSyncable(String account, String provider) for syncs that bind - * to a SyncService. - */ - void setServiceActive(in ComponentName cname, boolean active); - - /** - * Corresponds roughly to getIsSyncable(String account, String provider) for syncs that bind - * to a SyncService. - * @return 0 if this SyncService is not enabled, 1 if enabled, <0 if unknown. - */ - boolean isServiceActive(in ComponentName cname); - void setMasterSyncAutomatically(boolean flag); boolean getMasterSyncAutomatically(); + /** + * Returns true if there is currently a sync operation for the given + * account or authority in the pending list, or actively being processed. + */ + boolean isSyncActive(in Account account, String authority); + List getCurrentSyncs(); /** @@ -146,34 +130,18 @@ interface IContentService { */ SyncAdapterType[] getSyncAdapterTypes(); - /** - * Returns true if there is currently a operation for the given account/authority or service - * actively being processed. - * @param account account for authority, must be null if cname is non-null. - * @param providerName name of provider, must be null if cname is non-null. - * @param cname component to identify sync service, must be null if account/providerName are - * non-null. - */ - boolean isSyncActive(in Account account, String authority, in ComponentName cname); - /** * Returns the status that matches the authority. If there are multiples accounts for * the authority, the one with the latest "lastSuccessTime" status is returned. - * @param account account for authority, must be null if cname is non-null. - * @param providerName name of provider, must be null if cname is non-null. - * @param cname component to identify sync service, must be null if account/providerName are - * non-null. + * @param authority the authority whose row should be selected + * @return the SyncStatusInfo for the authority, or null if none exists */ - SyncStatusInfo getSyncStatus(in Account account, String authority, in ComponentName cname); + SyncStatusInfo getSyncStatus(in Account account, String authority); /** * Return true if the pending status is true of any matching authorities. - * @param account account for authority, must be null if cname is non-null. - * @param providerName name of provider, must be null if cname is non-null. - * @param cname component to identify sync service, must be null if account/providerName are - * non-null. */ - boolean isSyncPending(in Account account, String authority, in ComponentName cname); + boolean isSyncPending(in Account account, String authority); void addStatusChangeListener(int mask, ISyncStatusObserver callback); diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java index 836c6f8c6abfd..6aca151a1c1d1 100644 --- a/core/java/android/content/PeriodicSync.java +++ b/core/java/android/content/PeriodicSync.java @@ -35,7 +35,7 @@ public class PeriodicSync implements Parcelable { public final Bundle extras; /** How frequently the sync should be scheduled, in seconds. Kept around for API purposes. */ public final long period; - /** Whether this periodic sync runs on a {@link SyncService}. */ + /** Whether this periodic sync uses a service. */ public final boolean isService; /** * How much flexibility can be taken in scheduling the sync, in seconds. @@ -64,6 +64,8 @@ public class PeriodicSync implements Parcelable { this.flexTime = 0L; } + // TODO: Add copy ctor from SyncRequest? + /** * Create a copy of a periodic sync. * {@hide} @@ -181,8 +183,7 @@ public class PeriodicSync implements Parcelable { } /** - * Periodic sync extra comparison function. Duplicated from - * {@link com.android.server.content.SyncManager#syncExtrasEquals(Bundle b1, Bundle b2)} + * Periodic sync extra comparison function. * {@hide} */ public static boolean syncExtrasEquals(Bundle b1, Bundle b2) { diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java index 61b11c2be61bb..0284882963c72 100644 --- a/core/java/android/content/SyncInfo.java +++ b/core/java/android/content/SyncInfo.java @@ -17,7 +17,6 @@ package android.content; import android.accounts.Account; -import android.content.pm.RegisteredServicesCache; import android.os.Parcel; import android.os.Parcelable; import android.os.Parcelable.Creator; @@ -30,23 +29,15 @@ public class SyncInfo implements Parcelable { public final int authorityId; /** - * The {@link Account} that is currently being synced. Will be null if this sync is running via - * a {@link SyncService}. + * The {@link Account} that is currently being synced. */ public final Account account; /** - * The authority of the provider that is currently being synced. Will be null if this sync - * is running via a {@link SyncService}. + * The authority of the provider that is currently being synced. */ public final String authority; - /** - * The {@link SyncService} that is targeted by this operation. Null if this sync is running via - * a {@link AbstractThreadedSyncAdapter}. - */ - public final ComponentName service; - /** * The start time of the current sync operation in milliseconds since boot. * This is represented in elapsed real time. @@ -55,13 +46,12 @@ public class SyncInfo implements Parcelable { public final long startTime; /** @hide */ - public SyncInfo(int authorityId, Account account, String authority, ComponentName service, + public SyncInfo(int authorityId, Account account, String authority, long startTime) { this.authorityId = authorityId; this.account = account; this.authority = authority; this.startTime = startTime; - this.service = service; } /** @hide */ @@ -72,20 +62,17 @@ public class SyncInfo implements Parcelable { /** @hide */ public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(authorityId); - parcel.writeParcelable(account, flags); + account.writeToParcel(parcel, 0); parcel.writeString(authority); parcel.writeLong(startTime); - parcel.writeParcelable(service, flags); - } /** @hide */ SyncInfo(Parcel parcel) { authorityId = parcel.readInt(); - account = parcel.readParcelable(Account.class.getClassLoader()); + account = new Account(parcel); authority = parcel.readString(); startTime = parcel.readLong(); - service = parcel.readParcelable(ComponentName.class.getClassLoader()); } /** @hide */ diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java index b1d36d73c488b..4474c709e95ab 100644 --- a/core/java/android/content/SyncRequest.java +++ b/core/java/android/content/SyncRequest.java @@ -32,8 +32,8 @@ public class SyncRequest implements Parcelable { private final ComponentName mComponentInfo; /** Bundle containing user info as well as sync settings. */ private final Bundle mExtras; - /** Don't allow this sync request on metered networks. */ - private final boolean mDisallowMetered; + /** Allow this sync request on metered networks. */ + private final boolean mAllowMetered; /** * Anticipated upload size in bytes. * TODO: Not yet used - we put this information into the bundle for simplicity. @@ -85,32 +85,16 @@ public class SyncRequest implements Parcelable { /** * {@hide} + * Throws a runtime IllegalArgumentException if this function is called for an + * anonymous sync. * - * @return account object for this sync. - * @throws IllegalArgumentException if this function is called for a request that targets a - * sync service. + * @return (Account, Provider) for this SyncRequest. */ - public Account getAccount() { + public Pair getProviderInfo() { if (!hasAuthority()) { - throw new IllegalArgumentException("Cannot getAccount() for a sync that targets a sync" - + "service."); + throw new IllegalArgumentException("Cannot getProviderInfo() for an anonymous sync."); } - return mAccountToSync; - } - - /** - * {@hide} - * - * @return provider for this sync. - * @throws IllegalArgumentException if this function is called for a request that targets a - * sync service. - */ - public String getProvider() { - if (!hasAuthority()) { - throw new IllegalArgumentException("Cannot getProvider() for a sync that targets a" - + "sync service."); - } - return mAuthority; + return Pair.create(mAccountToSync, mAuthority); } /** @@ -175,7 +159,7 @@ public class SyncRequest implements Parcelable { parcel.writeLong(mSyncFlexTimeSecs); parcel.writeLong(mSyncRunTimeSecs); parcel.writeInt((mIsPeriodic ? 1 : 0)); - parcel.writeInt((mDisallowMetered ? 1 : 0)); + parcel.writeInt((mAllowMetered ? 1 : 0)); parcel.writeLong(mTxBytes); parcel.writeLong(mRxBytes); parcel.writeInt((mIsAuthority ? 1 : 0)); @@ -193,7 +177,7 @@ public class SyncRequest implements Parcelable { mSyncFlexTimeSecs = in.readLong(); mSyncRunTimeSecs = in.readLong(); mIsPeriodic = (in.readInt() != 0); - mDisallowMetered = (in.readInt() != 0); + mAllowMetered = (in.readInt() != 0); mTxBytes = in.readLong(); mRxBytes = in.readLong(); mIsAuthority = (in.readInt() != 0); @@ -223,7 +207,7 @@ public class SyncRequest implements Parcelable { // For now we merge the sync config extras & the custom extras into one bundle. // TODO: pass the configuration extras through separately. mExtras.putAll(b.mSyncConfigExtras); - mDisallowMetered = b.mDisallowMetered; + mAllowMetered = b.mAllowMetered; mTxBytes = b.mTxBytes; mRxBytes = b.mRxBytes; } @@ -269,7 +253,7 @@ public class SyncRequest implements Parcelable { /** Expected download transfer in bytes. */ private long mRxBytes = -1L; /** Whether or not this sync can occur on metered networks. Default false. */ - private boolean mDisallowMetered; + private boolean mAllowMetered; /** Priority of this sync relative to others from calling app [-2, 2]. Default 0. */ private int mPriority = 0; /** @@ -360,10 +344,6 @@ public class SyncRequest implements Parcelable { * You cannot reuse the same builder for one-time syncs after having specified a periodic * sync (by calling this function). If you do, an IllegalArgumentException * will be thrown. - *

The bundle for a periodic sync can be queried by applications with the correct - * permissions using - * {@link ContentResolver#getPeriodicSyncs(Account account, String provider)}, so no - * sensitive data should be transferred here. * * Example usage. * @@ -427,17 +407,10 @@ public class SyncRequest implements Parcelable { } /** - * Will throw an IllegalArgumentException if called and - * {@link #setIgnoreSettings(boolean ignoreSettings)} has already been called. - * @param disallow true to allow this transfer on metered networks. Default false. - * + * @param allow false to allow this transfer on metered networks. Default true. */ - public Builder setDisallowMetered(boolean disallow) { - if (mIgnoreSettings && disallow) { - throw new IllegalArgumentException("setDisallowMetered(true) after having" - + "specified that settings are ignored."); - } - mDisallowMetered = disallow; + public Builder setAllowMetered(boolean allow) { + mAllowMetered = true; return this; } @@ -538,17 +511,10 @@ public class SyncRequest implements Parcelable { * * Not valid for periodic sync and will throw an IllegalArgumentException in * {@link #build()}. - *

Throws IllegalArgumentException if called and - * {@link #setDisallowMetered(false)} has been set. - * * * @param ignoreSettings true to ignore the sync automatically settings. Default false. */ public Builder setIgnoreSettings(boolean ignoreSettings) { - if (mDisallowMetered && ignoreSettings) { - throw new IllegalArgumentException("setIgnoreSettings(true) after having specified" - + " sync settings with this builder."); - } mIgnoreSettings = ignoreSettings; return this; } @@ -625,8 +591,8 @@ public class SyncRequest implements Parcelable { if (mIgnoreBackoff) { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); } - if (mDisallowMetered) { - mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, true); + if (mAllowMetered) { + mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_ALLOW_METERED, true); } if (mIgnoreSettings) { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); @@ -638,27 +604,18 @@ public class SyncRequest implements Parcelable { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); } if (mIsManual) { - mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); - mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); + mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); } mSyncConfigExtras.putLong(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD, mTxBytes); mSyncConfigExtras.putLong(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD, mRxBytes); mSyncConfigExtras.putInt(ContentResolver.SYNC_EXTRAS_PRIORITY, mPriority); if (mSyncType == SYNC_TYPE_PERIODIC) { // If this is a periodic sync ensure than invalid extras were not set. - if (ContentResolver.invalidPeriodicExtras(mCustomExtras) || - ContentResolver.invalidPeriodicExtras(mSyncConfigExtras)) { - throw new IllegalArgumentException("Illegal extras were set"); - } + validatePeriodicExtras(mCustomExtras); + validatePeriodicExtras(mSyncConfigExtras); } else if (mSyncType == SYNC_TYPE_UNKNOWN) { throw new IllegalArgumentException("Must call either syncOnce() or syncPeriodic()"); } - if (mSyncTarget == SYNC_TARGET_SERVICE) { - if (mSyncConfigExtras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) { - throw new IllegalArgumentException("Cannot specify an initialisation sync" - + " that targets a service."); - } - } // Ensure that a target for the sync has been set. if (mSyncTarget == SYNC_TARGET_UNKNOWN) { throw new IllegalArgumentException("Must specify an adapter with one of" @@ -666,5 +623,23 @@ public class SyncRequest implements Parcelable { } return new SyncRequest(this); } - } + + /** + * Helper function to throw an IllegalArgumentException if any illegal + * extras were set for a periodic sync. + * + * @param extras bundle to validate. + */ + private void validatePeriodicExtras(Bundle extras) { + if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false) + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false) + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false) + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false) + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false) + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false) + || extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) { + throw new IllegalArgumentException("Illegal extras were set"); + } + } + } } diff --git a/core/java/android/content/SyncService.java b/core/java/android/content/SyncService.java index f7655ee60797f..100fd40cf6946 100644 --- a/core/java/android/content/SyncService.java +++ b/core/java/android/content/SyncService.java @@ -21,31 +21,24 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Process; import android.os.Trace; -import android.util.ArrayMap; -import android.util.SparseArray; -import android.util.Log; import com.android.internal.annotations.GuardedBy; +import java.util.HashMap; + /** * Simplified @link android.content.AbstractThreadedSyncAdapter. Folds that * behaviour into a service to which the system can bind when requesting an * anonymous (providerless/accountless) sync. *

- * In order to perform an anonymous sync operation you must extend this service, implementing the - * abstract methods. This service must be declared in the application's manifest as usual. You - * can use this service for other work, however you must not override the onBind() method - * unless you know what you're doing, which limits the usefulness of this service for other work. - *

A {@link SyncService} can either be enabled, disabled, or have unknown state. This is similar - * to an {@link AbstractThreadedSyncAdapter} isSyncable state, where the service is considered in - * an unknown state until run for the first time. Implementers of this class can override - * {@link #onPerformInitialisation(Bundle)} to change this behaviour. Different to an - * {@link AbstractThreadedSyncAdapter} however, there is no - * {@link ContentResolver#setSyncAutomatically(android.accounts.Account account, String provider, boolean sync)}. - * This functionality is collapsed into the enabled/disabled state of the SyncService. + * In order to perform an anonymous sync operation you must extend this service, + * implementing the abstract methods. This service must then be declared in the + * application's manifest as usual. You can use this service for other work, however you + * must not override the onBind() method unless you know what you're doing, + * which limits the usefulness of this service for other work. * *

- * <service android:name=".MySyncService"/>
+ * <service ndroid:name=".MyAnonymousSyncService" android:permission="android.permission.SYNC" />
  * 
* Like @link android.content.AbstractThreadedSyncAdapter this service supports * multiple syncs at the same time. Each incoming startSync() with a unique tag @@ -55,50 +48,41 @@ import com.android.internal.annotations.GuardedBy; * at once, so if you mutate local objects you must ensure synchronization. */ public abstract class SyncService extends Service { - private static final String TAG = "SyncService"; - private final SyncAdapterImpl mSyncAdapter = new SyncAdapterImpl(); + /** SyncAdapter Instantiation that any anonymous syncs call. */ + private final AnonymousSyncAdapterImpl mSyncAdapter = new AnonymousSyncAdapterImpl(); - /** Keep track of on-going syncs, keyed by bundle. */ - @GuardedBy("mSyncThreadLock") - private final SparseArray - mSyncThreads = new SparseArray(); + /** Keep track of on-going syncs, keyed by tag. */ + @GuardedBy("mLock") + private final HashMap + mSyncThreads = new HashMap(); /** Lock object for accessing the SyncThreads HashMap. */ private final Object mSyncThreadLock = new Object(); - /** - * Default key for if this sync service does not support parallel operations. Currently not - * sure if null keys will make it into the ArrayMap for KLP, so keeping our default for now. - */ - private static final int KEY_DEFAULT = 0; - /** Identifier for this sync service. */ - private ComponentName mServiceComponent; - /** {@hide} */ + @Override public IBinder onBind(Intent intent) { - mServiceComponent = new ComponentName(this, getClass()); return mSyncAdapter.asBinder(); } /** {@hide} */ - private class SyncAdapterImpl extends ISyncServiceAdapter.Stub { + private class AnonymousSyncAdapterImpl extends IAnonymousSyncAdapter.Stub { + @Override public void startSync(ISyncContext syncContext, Bundle extras) { // Wrap the provided Sync Context because it may go away by the time // we call it. final SyncContext syncContextClient = new SyncContext(syncContext); boolean alreadyInProgress = false; - final int extrasAsKey = extrasToKey(extras); synchronized (mSyncThreadLock) { - if (mSyncThreads.get(extrasAsKey) != null) { - Log.e(TAG, "starting sync for : " + mServiceComponent); - // Start sync. - SyncThread syncThread = new SyncThread(syncContextClient, extras); - mSyncThreads.put(extrasAsKey, syncThread); - syncThread.start(); - } else { + if (mSyncThreads.containsKey(extras)) { // Don't want to call back to SyncManager while still // holding lock. alreadyInProgress = true; + } else { + AnonymousSyncThread syncThread = new AnonymousSyncThread( + syncContextClient, extras); + mSyncThreads.put(extras, syncThread); + syncThread.start(); } } if (alreadyInProgress) { @@ -107,15 +91,14 @@ public abstract class SyncService extends Service { } /** - * Used by the SM to cancel a specific sync using the - * com.android.server.content.SyncManager.ActiveSyncContext as a handle. + * Used by the SM to cancel a specific sync using the {@link + * com.android.server.content.SyncManager.ActiveSyncContext} as a handle. */ @Override public void cancelSync(ISyncContext syncContext) { - SyncThread runningSync = null; + AnonymousSyncThread runningSync = null; synchronized (mSyncThreadLock) { - for (int i = 0; i < mSyncThreads.size(); i++) { - SyncThread thread = mSyncThreads.valueAt(i); + for (AnonymousSyncThread thread : mSyncThreads.values()) { if (thread.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) { runningSync = thread; break; @@ -128,40 +111,20 @@ public abstract class SyncService extends Service { } } - /** - * - * @param extras Bundle for which to compute hash - * @return an integer hash that is equal to that of another bundle if they both contain the - * same key -> value mappings, however, not necessarily in order. - * Based on the toString() representation of the value mapped. - */ - private int extrasToKey(Bundle extras) { - int hash = KEY_DEFAULT; // Empty bundle, or no parallel operations enabled. - if (parallelSyncsEnabled()) { - for (String key : extras.keySet()) { - String mapping = key + " " + extras.get(key).toString(); - hash += mapping.hashCode(); - } - } - return hash; - } - /** * {@hide} * Similar to {@link android.content.AbstractThreadedSyncAdapter.SyncThread}. However while * the ATSA considers an already in-progress sync to be if the account provided is currently - * syncing, this anonymous sync has no notion of account and considers a sync unique if the - * provided bundle is different. + * syncing, this anonymous sync has no notion of account and therefore considers a sync unique + * if the provided bundle is different. */ - private class SyncThread extends Thread { + private class AnonymousSyncThread extends Thread { private final SyncContext mSyncContext; private final Bundle mExtras; - private final int mThreadsKey; - public SyncThread(SyncContext syncContext, Bundle extras) { + public AnonymousSyncThread(SyncContext syncContext, Bundle extras) { mSyncContext = syncContext; mExtras = extras; - mThreadsKey = extrasToKey(extras); } @Override @@ -172,8 +135,10 @@ public abstract class SyncService extends Service { SyncResult syncResult = new SyncResult(); try { - if (isCancelled()) return; - // Run the sync. + if (isCancelled()) { + return; + } + // Run the sync based off of the provided code. SyncService.this.onPerformSync(mExtras, syncResult); } finally { Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER); @@ -181,9 +146,10 @@ public abstract class SyncService extends Service { mSyncContext.onFinished(syncResult); } // Synchronize so that the assignment will be seen by other - // threads that also synchronize accesses to mSyncThreads. + // threads + // that also synchronize accesses to mSyncThreads. synchronized (mSyncThreadLock) { - mSyncThreads.remove(mThreadsKey); + mSyncThreads.remove(mExtras); } } } @@ -200,14 +166,4 @@ public abstract class SyncService extends Service { */ public abstract void onPerformSync(Bundle extras, SyncResult syncResult); - /** - * Override this function to indicated whether you want to support parallel syncs. - *

If you override and return true multiple threads will be spawned within your Service to - * handle each concurrent sync request. - * - * @return false to indicate that this service does not support parallel operations by default. - */ - protected boolean parallelSyncsEnabled() { - return false; - } } diff --git a/services/java/com/android/server/content/ContentService.java b/services/java/com/android/server/content/ContentService.java index ab20461e31142..a56af08a3da8a 100644 --- a/services/java/com/android/server/content/ContentService.java +++ b/services/java/com/android/server/content/ContentService.java @@ -25,8 +25,6 @@ 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; @@ -317,6 +315,7 @@ public final class ContentService extends IContentService.Stub { } } + @Override public void requestSync(Account account, String authority, Bundle extras) { ContentResolver.validateSyncExtrasBundle(extras); int userId = UserHandle.getCallingUserId(); @@ -346,56 +345,61 @@ 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 callerUid = Binder.getCallingUid(); + int uId = 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) 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 (syncManager != null) { if (request.hasAuthority()) { - syncManager.scheduleSync( - request.getAccount(), userId, callerUid, request.getProvider(), extras, - beforeRuntimeMillis, runtimeMillis, - false /* onlyThoseWithUnknownSyncableState */); + // 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 */); + } } else { - syncManager.scheduleSync( - request.getService(), userId, callerUid, extras, - beforeRuntimeMillis, - runtimeMillis); // Empty function. + // 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."); + } } } } finally { @@ -406,13 +410,11 @@ 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, 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. + * @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 */ - public void cancelSync(Account account, String authority, ComponentName cname) { + @Override + public void cancelSync(Account account, String authority) { int userId = UserHandle.getCallingUserId(); // This makes it so that future permission checks will be in the context of this @@ -421,54 +423,14 @@ public final class ContentService extends IContentService.Stub { try { SyncManager syncManager = getSyncManager(); if (syncManager != null) { - 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 */); + syncManager.clearScheduledSyncOperations(account, userId, authority); + syncManager.cancelActiveSync(account, userId, authority); } } 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 @@ -497,8 +459,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); @@ -516,15 +478,18 @@ 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. */ + /** + * Old API. Schedule periodic sync with default flex time. + */ + @Override public void addPeriodicSync(Account account, String authority, Bundle extras, long pollFrequency) { mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, @@ -536,22 +501,21 @@ 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 { - SyncStorageEngine.EndPoint info = - new SyncStorageEngine.EndPoint(account, authority, userId); - getSyncManager().getSyncStorageEngine() - .updateOrAddPeriodicSync(info, - pollFrequency, - defaultFlex, - extras); + // Add default flex time to this sync. + PeriodicSync syncToAdd = + new PeriodicSync(account, authority, extras, + pollFrequency, + SyncStorageEngine.calculateDefaultFlexTime(pollFrequency)); + getSyncManager().getSyncStorageEngine().addPeriodicSync(syncToAdd, userId); } 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"); @@ -559,34 +523,32 @@ public final class ContentService extends IContentService.Stub { long identityToken = clearCallingIdentity(); try { - getSyncManager().getSyncStorageEngine() - .removePeriodicSync( - new SyncStorageEngine.EndPoint(account, authority, userId), - extras); + PeriodicSync syncToRemove = new PeriodicSync(account, authority, extras, + 0 /* Not read for removal */, 0 /* Not read for removal */); + getSyncManager().getSyncStorageEngine().removePeriodicSync(syncToRemove, userId); } finally { restoreCallingIdentity(identityToken); } } - public List getPeriodicSyncs(Account account, String providerName, - ComponentName cname) { + /** + * TODO: Implement. + * @param request Sync to remove. + */ + public void removeSync(SyncRequest request) { + + } + + @Override + public List getPeriodicSyncs(Account account, String providerName) { 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 { - 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"); - } + return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( + account, userId, providerName); } finally { restoreCallingIdentity(identityToken); } @@ -610,6 +572,7 @@ 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"); @@ -627,43 +590,6 @@ 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, @@ -699,24 +625,17 @@ public final class ContentService extends IContentService.Stub { } } - public boolean isSyncActive(Account account, String authority, ComponentName cname) { + public boolean isSyncActive(Account account, String authority) { 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) { - return false; - } - if (cname == null) { + if (syncManager != null) { return syncManager.getSyncStorageEngine().isSyncActive( - 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)); + account, userId, authority); } } finally { restoreCallingIdentity(identityToken); @@ -737,55 +656,39 @@ public final class ContentService extends IContentService.Stub { } } - public SyncStatusInfo getSyncStatus(Account account, String authority, ComponentName cname) { + public SyncStatusInfo getSyncStatus(Account account, String authority) { 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 null; + if (syncManager != null) { + return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority( + 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(), "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, ComponentName cname) { + public boolean isSyncPending(Account account, String authority) { 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; + long identityToken = clearCallingIdentity(); try { - 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"); + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority); } - return syncManager.getSyncStorageEngine().isSyncPending(info); } finally { restoreCallingIdentity(identityToken); } + return false; } public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { @@ -818,30 +721,6 @@ public final class ContentService extends IContentService.Stub { return service; } - /** - * 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 diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java index 1fd02da78e378..ee5b8907f84e8 100644 --- a/services/java/com/android/server/content/SyncManager.java +++ b/services/java/com/android/server/content/SyncManager.java @@ -31,7 +31,6 @@ 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; @@ -201,9 +200,8 @@ public class SyncManager { Log.v(TAG, "Internal storage is low."); } mStorageIsLow = true; - cancelActiveSync( - SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL, - null /* any sync */); + cancelActiveSync(null /* any account */, UserHandle.USER_ALL, + null /* any authority */); } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Internal storage is ok."); @@ -221,6 +219,19 @@ 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) { @@ -265,16 +276,16 @@ public class SyncManager { doDatabaseCleanup(); } - AccountAndUser[] accounts = mRunningAccounts; for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { - if (!containsAccountAndUser(accounts, - currentSyncContext.mSyncOperation.target.account, - currentSyncContext.mSyncOperation.target.userId)) { + if (!containsAccountAndUser(mRunningAccounts, + currentSyncContext.mSyncOperation.account, + currentSyncContext.mSyncOperation.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(); @@ -369,17 +380,12 @@ public class SyncManager { mSyncStorageEngine = SyncStorageEngine.getSingleton(); mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() { @Override - 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 */); - } + 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); } }); @@ -411,6 +417,9 @@ 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); @@ -523,160 +532,38 @@ 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 milliseconds before runtimeMillis that this sync may - * be run. - * @param runtimeMillis milliseconds from now by which this sync must be run. + * @param beforeRunTimeMillis + * @param runtimeMillis */ public void scheduleSync(ComponentName cname, int userId, int uid, Bundle extras, - long beforeRunTimeMillis, long runtimeMillis) { + long beforeRunTimeMillis, long runtimeMillis, + boolean onlyThoseWithUnknownSyncableState) { +/** boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); - if (isLoggable) { - 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 - } - - final boolean ignoreSettings = - extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false); - 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; - } - 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 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); - } - 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)); - } - - } - - /** - * Initiate a sync. This can start a sync for all providers - * (pass null to url, set onlyTicklable to false), only those - * providers that are marked as ticklable (pass null to url, - * set onlyTicklable to true), or a specific provider (set url - * to the content url of the provider). - * - *

If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is - * true then initiate a sync that just checks for local changes to send - * to the server, otherwise initiate a sync that first gets any - * changes from the server before sending local changes back to - * the server. - * - *

If a specific provider is being synced (the url is non-null) - * then the extras can contain SyncAdapter-specific information - * to control what gets synced (e.g. which specific feed to sync). - * - *

You'll start getting callbacks after this. - * - * @param requestedAccount the account to sync, may be null to signify all accounts - * @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 reason for sync request. If this is a positive integer, it is the Linux uid - * assigned to the process that requested the sync. If it's negative, the sync was requested by - * the SyncManager itself and could be one of the following: - * {@link SyncOperation#REASON_BACKGROUND_DATA_SETTINGS_CHANGED} - * {@link SyncOperation#REASON_ACCOUNTS_UPDATED} - * {@link SyncOperation#REASON_SERVICE_CHANGED} - * {@link SyncOperation#REASON_PERIODIC} - * {@link SyncOperation#REASON_IS_SYNCABLE} - * {@link SyncOperation#REASON_SYNC_AUTO} - * {@link SyncOperation#REASON_MASTER_SYNC_AUTO} - * {@link SyncOperation#REASON_USER_START} - * @param requestedAuthority the authority to sync, may be null to indicate all authorities - * @param extras a Map of SyncAdapter-specific information to control - * syncs of a specific provider. Can be null. Is ignored - * if the url is null. - * @param beforeRuntimeMillis milliseconds before runtimeMillis that this sync can run. - * @param runtimeMillis maximum milliseconds in the future to wait before performing sync. - * @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state. - */ - public void scheduleSync(Account requestedAccount, int userId, int reason, - String requestedAuthority, Bundle extras, long beforeRuntimeMillis, - long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) { - boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); + final boolean backgroundDataUsageAllowed = !mBootCompleted || + getConnectivityManager().getBackgroundDataSetting(); if (extras == null) { extras = new Bundle(); } if (isLoggable) { - Log.d(TAG, "one-time sync for: " + requestedAccount + " " + extras.toString() + " " - + requestedAuthority); + Log.e(TAG, requestedAccount + " " + extras.toString() + " " + requestedAuthority); } Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false); if (expedited) { @@ -687,6 +574,8 @@ 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) { @@ -761,10 +650,12 @@ public class SyncManager { continue; } + // always allow if the isSyncable state is unknown boolean syncAllowed = - (isSyncable < 0) // always allow if the isSyncable state is unknown + (isSyncable < 0) || ignoreSettings - || (mSyncStorageEngine.getMasterSyncAutomatically(account.userId) + || (backgroundDataUsageAllowed + && mSyncStorageEngine.getMasterSyncAutomatically(account.userId) && mSyncStorageEngine.getSyncAutomatically(account.account, account.userId, authority)); if (!syncAllowed) { @@ -774,12 +665,211 @@ public class SyncManager { } continue; } - SyncStorageEngine.EndPoint info = - new SyncStorageEngine.EndPoint( - account.account, authority, account.userId); - Pair backoff = mSyncStorageEngine.getBackoff(info); - long delayUntil = - mSyncStorageEngine.getDelayUntilTime(info); + + Pair 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)); + } + } + }*/ + } + + /** + * Initiate a sync. This can start a sync for all providers + * (pass null to url, set onlyTicklable to false), only those + * providers that are marked as ticklable (pass null to url, + * set onlyTicklable to true), or a specific provider (set url + * to the content url of the provider). + * + *

If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is + * true then initiate a sync that just checks for local changes to send + * to the server, otherwise initiate a sync that first gets any + * changes from the server before sending local changes back to + * the server. + * + *

If a specific provider is being synced (the url is non-null) + * then the extras can contain SyncAdapter-specific information + * to control what gets synced (e.g. which specific feed to sync). + * + *

You'll start getting callbacks after this. + * + * @param requestedAccount the account to sync, may be null to signify all accounts + * @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 reason for sync request. If this is a positive integer, it is the Linux uid + * assigned to the process that requested the sync. If it's negative, the sync was requested by + * the SyncManager itself and could be one of the following: + * {@link SyncOperation#REASON_BACKGROUND_DATA_SETTINGS_CHANGED} + * {@link SyncOperation#REASON_ACCOUNTS_UPDATED} + * {@link SyncOperation#REASON_SERVICE_CHANGED} + * {@link SyncOperation#REASON_PERIODIC} + * {@link SyncOperation#REASON_IS_SYNCABLE} + * {@link SyncOperation#REASON_SYNC_AUTO} + * {@link SyncOperation#REASON_MASTER_SYNC_AUTO} + * {@link SyncOperation#REASON_USER_START} + * @param requestedAuthority the authority to sync, may be null to indicate all authorities + * @param extras a Map of SyncAdapter-specific information to control + * syncs of a specific provider. Can be null. Is ignored + * if the url is null. + * @param beforeRuntimeMillis milliseconds before runtimeMillis that this sync can run. + * @param runtimeMillis maximum milliseconds in the future to wait before performing sync. + * @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state. + */ + public void scheduleSync(Account requestedAccount, int userId, int reason, + String requestedAuthority, Bundle extras, long beforeRuntimeMillis, + long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) { + boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); + + final boolean backgroundDataUsageAllowed = !mBootCompleted || + getConnectivityManager().getBackgroundDataSetting(); + + if (extras == null) { + extras = new Bundle(); + } + if (isLoggable) { + Log.d(TAG, "one-time sync for: " + requestedAccount + " " + extras.toString() + " " + + requestedAuthority); + } + 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; + } + + 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 syncableAuthorities = new HashSet(); + for (RegisteredServicesCache.ServiceInfo syncAdapter : + mSyncAdapters.getAllServices(account.userId)) { + syncableAuthorities.add(syncAdapter.type.authority); + } + + // 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); + } + + for (String authority : syncableAuthorities) { + int isSyncable = getIsSyncable(account.account, account.userId, + authority); + if (isSyncable == 0) { + continue; + } + final RegisteredServicesCache.ServiceInfo 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 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. @@ -789,7 +879,6 @@ public class SyncManager { Log.v(TAG, "schedule initialisation Sync:" + ", delay until " + delayUntil + ", run by " + 0 - + ", flex " + 0 + ", source " + source + ", account " + account + ", authority " + authority @@ -865,12 +954,13 @@ public class SyncManager { mSyncHandler.sendMessage(msg); } - private void sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras) { + private void sendCancelSyncsMessage(final Account account, final int userId, + final String authority) { if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL"); Message msg = mSyncHandler.obtainMessage(); msg.what = SyncHandler.MESSAGE_CANCEL; - msg.setData(extras); - msg.obj = info; + msg.obj = Pair.create(account, authority); + msg.arg1 = userId; mSyncHandler.sendMessage(msg); } @@ -893,11 +983,10 @@ public class SyncManager { } private void clearBackoffSetting(SyncOperation op) { - mSyncStorageEngine.setBackoff(op.target, - SyncStorageEngine.NOT_IN_BACKOFF_MODE, - SyncStorageEngine.NOT_IN_BACKOFF_MODE); + mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, + SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE); synchronized (mSyncQueue) { - mSyncQueue.onBackoffChanged(op.target, 0); + mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, 0); } } @@ -907,7 +996,7 @@ public class SyncManager { final long now = SystemClock.elapsedRealtime(); final Pair previousSettings = - mSyncStorageEngine.getBackoff(op.target); + mSyncStorageEngine.getBackoff(op.account, op.userId, op.authority); long newDelayInMs = -1; if (previousSettings != null) { // don't increase backoff before current backoff is expired. This will happen for op's @@ -938,12 +1027,14 @@ public class SyncManager { final long backoff = now + newDelayInMs; - mSyncStorageEngine.setBackoff(op.target, backoff, newDelayInMs); + mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority, + backoff, newDelayInMs); + op.backoff = backoff; op.updateEffectiveRunTime(); synchronized (mSyncQueue) { - mSyncQueue.onBackoffChanged(op.target, backoff); + mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, backoff); } } @@ -956,20 +1047,20 @@ public class SyncManager { } else { newDelayUntilTime = 0; } - mSyncStorageEngine.setDelayUntilTime(op.target, newDelayUntilTime); + mSyncStorageEngine + .setDelayUntilTime(op.account, op.userId, op.authority, newDelayUntilTime); synchronized (mSyncQueue) { - mSyncQueue.onDelayUntilTimeChanged(op.target, newDelayUntilTime); + mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime); } } /** * Cancel the active sync if it matches the authority and account. - * @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. + * @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 */ - public void cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras) { - sendCancelSyncsMessage(info, extras); + public void cancelActiveSync(Account account, int userId, String authority) { + sendCancelSyncsMessage(account, userId, authority); } /** @@ -998,40 +1089,24 @@ public class SyncManager { /** * Remove scheduled sync operations. - * @param info limit the removals to operations that match this authority. The authority can - * have null account/provider info to specify all accounts/providers. + * @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 */ - public void clearScheduledSyncOperations(SyncStorageEngine.EndPoint info) { + public void clearScheduledSyncOperations(Account account, int userId, String authority) { synchronized (mSyncQueue) { - mSyncQueue.remove(info, null /* all operations */); + mSyncQueue.remove(account, userId, authority); } - mSyncStorageEngine.setBackoff(info, + mSyncStorageEngine.setBackoff(account, userId, authority, 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, 0L /* newRunTimeFromNow */); + operation = new SyncOperation(operation); // 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 @@ -1040,29 +1115,25 @@ 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)) { - if (isLoggable) { - Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified " - + operation); - } + 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); - if (isLoggable) { - Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync " - + "encountered an error: " + operation); - } + 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) { - // 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); - } + 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"); @@ -1075,18 +1146,19 @@ public class SyncManager { } scheduleSyncOperation( new SyncOperation( - operation, - DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000 /* newRunTimeFromNow */) - ); + 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)); } 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); } @@ -1119,12 +1191,9 @@ public class SyncManager { updateRunningAccounts(); cancelActiveSync( - new SyncStorageEngine.EndPoint( - null /* any account */, - null /* any authority */, - userId), - null /* any sync. */ - ); + null /* any account */, + userId, + null /* any authority */); } private void onUserRemoved(int userId) { @@ -1133,7 +1202,7 @@ public class SyncManager { // Clean up the storage engine database mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId); synchronized (mSyncQueue) { - mSyncQueue.removeUserLocked(userId); + mSyncQueue.removeUser(userId); } } @@ -1145,7 +1214,6 @@ public class SyncManager { final SyncOperation mSyncOperation; final long mHistoryRowId; ISyncAdapter mSyncAdapter; - ISyncServiceAdapter mSyncServiceAdapter; final long mStartTime; long mTimeoutStartTime; boolean mBound; @@ -1171,10 +1239,10 @@ public class SyncManager { mSyncOperation = syncOperation; mHistoryRowId = historyRowId; mSyncAdapter = null; - mSyncServiceAdapter = null; mStartTime = SystemClock.elapsedRealtime(); mTimeoutStartTime = mStartTime; - mSyncWakeLock = mSyncHandler.getSyncWakeLock(mSyncOperation); + mSyncWakeLock = mSyncHandler.getSyncWakeLock( + mSyncOperation.account, mSyncOperation.authority); mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid)); mSyncWakeLock.acquire(); } @@ -1201,12 +1269,7 @@ public class SyncManager { public void onServiceConnected(ComponentName name, IBinder service) { Message msg = mSyncHandler.obtainMessage(); msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED; - if (mSyncOperation.target.target_provider) { - msg.arg1 = SyncOperation.SYNC_TARGET_ADAPTER; - } else { - msg.arg1 = SyncOperation.SYNC_TARGET_SERVICE; - } - msg.obj = new ServiceConnectionData(this, service); + msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service)); mSyncHandler.sendMessage(msg); } @@ -1217,13 +1280,13 @@ public class SyncManager { mSyncHandler.sendMessage(msg); } - boolean bindToSyncAdapter(ComponentName serviceComponent, int userId) { + boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) { if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this); + Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this); } Intent intent = new Intent(); intent.setAction("android.content.SyncAdapter"); - intent.setComponent(serviceComponent); + intent.setComponent(info.componentName); intent.putExtra(Intent.EXTRA_CLIENT_LABEL, com.android.internal.R.string.sync_binding_label); intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser( @@ -1233,7 +1296,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.target.userId)); + new UserHandle(mSyncOperation.userId)); if (!bindResult) { mBound = false; } @@ -1256,6 +1319,7 @@ public class SyncManager { mSyncWakeLock.setWorkSource(null); } + @Override public String toString() { StringBuilder sb = new StringBuilder(); toString(sb); @@ -1394,14 +1458,11 @@ public class SyncManager { int row = table.getNumRows(); Pair syncAuthoritySyncStatus = mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus( - new SyncStorageEngine.EndPoint( - account.account, - syncAdapterType.type.authority, - account.userId)); + account.account, account.userId, syncAdapterType.type.authority); SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first; SyncStatusInfo status = syncAuthoritySyncStatus.second; - String authority = settings.base.provider; + String authority = settings.authority; if (authority.length() > 50) { authority = authority.substring(authority.length() - 50); } @@ -1522,25 +1583,14 @@ public class SyncManager { int maxAuthority = 0; int maxAccount = 0; for (SyncStorageEngine.SyncHistoryItem item : items) { - SyncStorageEngine.AuthorityInfo authorityInfo + SyncStorageEngine.AuthorityInfo authority = mSyncStorageEngine.getAuthority(item.authorityId); final String authorityName; final String accountKey; - 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"; - } + if (authority != null) { + authorityName = authority.authority; + accountKey = authority.account.name + "/" + authority.account.type + + " u" + authority.userId; } else { authorityName = "Unknown"; accountKey = "Unknown"; @@ -1661,25 +1711,14 @@ public class SyncManager { final PackageManager pm = mContext.getPackageManager(); for (int i = 0; i < N; i++) { SyncStorageEngine.SyncHistoryItem item = items.get(i); - SyncStorageEngine.AuthorityInfo authorityInfo + SyncStorageEngine.AuthorityInfo authority = mSyncStorageEngine.getAuthority(item.authorityId); final String authorityName; final String accountKey; - 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"; - } + if (authority != null) { + authorityName = authority.authority; + accountKey = authority.account.name + "/" + authority.account.type + + " u" + authority.userId; } else { authorityName = "Unknown"; accountKey = "Unknown"; @@ -1738,25 +1777,14 @@ public class SyncManager { if (extras == null || extras.size() == 0) { continue; } - final SyncStorageEngine.AuthorityInfo authorityInfo + final SyncStorageEngine.AuthorityInfo authority = mSyncStorageEngine.getAuthority(item.authorityId); final String authorityName; final String accountKey; - 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"; - } + if (authority != null) { + authorityName = authority.authority; + accountKey = authority.account.name + "/" + authority.account.type + + " u" + authority.userId; } else { authorityName = "Unknown"; accountKey = "Unknown"; @@ -1900,11 +1928,10 @@ public class SyncManager { class ServiceConnectionData { public final ActiveSyncContext activeSyncContext; - public final IBinder adapter; - - ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) { + public final ISyncAdapter syncAdapter; + ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) { this.activeSyncContext = activeSyncContext; - this.adapter = adapter; + this.syncAdapter = syncAdapter; } } @@ -1924,7 +1951,8 @@ public class SyncManager { public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo(); private Long mAlarmScheduleTime = null; public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker(); - private final HashMap mWakeLocks = Maps.newHashMap(); + private final HashMap, PowerManager.WakeLock> mWakeLocks = + Maps.newHashMap(); private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1); @@ -1938,11 +1966,12 @@ public class SyncManager { } } - private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) { - final String wakeLockKey = operation.wakeLockKey(); + private PowerManager.WakeLock getSyncWakeLock(Account account, String authority) { + final Pair wakeLockKey = Pair.create(account, authority); PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey); if (wakeLock == null) { - final String name = SYNC_WAKE_LOCK_PREFIX + operation.wakeLockName(); + final String name = SYNC_WAKE_LOCK_PREFIX + "/" + authority + "/" + account.type + + "/" + account.name; wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); wakeLock.setReferenceCounted(false); mWakeLocks.put(wakeLockKey, wakeLock); @@ -1991,6 +2020,7 @@ public class SyncManager { super(looper); } + @Override public void handleMessage(Message msg) { long earliestFuturePollTime = Long.MAX_VALUE; long nextPendingSyncTime = Long.MAX_VALUE; @@ -2008,13 +2038,12 @@ public class SyncManager { earliestFuturePollTime = scheduleReadyPeriodicSyncs(); switch (msg.what) { case SyncHandler.MESSAGE_CANCEL: { - SyncStorageEngine.EndPoint payload = (SyncStorageEngine.EndPoint) msg.obj; - Bundle extras = msg.peekData(); + Pair payload = (Pair) msg.obj; if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: " - + payload + " bundle: " + extras); + + payload.first + ", " + payload.second); } - cancelActiveSyncLocked(payload, extras); + cancelActiveSyncLocked(payload.first, msg.arg1, payload.second); nextPendingSyncTime = maybeStartNextSyncLocked(); break; } @@ -2023,39 +2052,35 @@ 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)) { - runBoundToAdapter( - msgData.activeSyncContext, - msgData.adapter, - msg.arg1 /* target */); + runBoundToSyncAdapter(msgData.activeSyncContext, msgData.syncAdapter); } 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); @@ -2064,15 +2089,12 @@ public class SyncManager { if (isSyncStillActive(currentSyncContext)) { // cancel the sync if we have a syncadapter, which means one is // outstanding - try { - if (currentSyncContext.mSyncAdapter != null) { + if (currentSyncContext.mSyncAdapter != null) { + try { currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext); - } else if (currentSyncContext.mSyncServiceAdapter != null) { - currentSyncContext.mSyncServiceAdapter - .cancelSync(currentSyncContext); + } catch (RemoteException e) { + // we don't need to retry this in this case } - } catch (RemoteException e) { - // We don't need to retry this in this case. } // pretend that the sync failed with an IOException, @@ -2117,46 +2139,9 @@ 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() { @@ -2164,7 +2149,14 @@ 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) @@ -2175,8 +2167,23 @@ public class SyncManager { for (Pair info : infos) { final AuthorityInfo authorityInfo = info.first; final SyncStatusInfo status = info.second; - - if (!isDispatchable(authorityInfo.base)) { + // 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) { continue; } @@ -2204,7 +2211,7 @@ public class SyncManager { boolean runEarly = remainingMillis <= flexInMillis && timeSinceLastRunMillis > periodInMillis - flexInMillis; if (isLoggable) { - Log.v(TAG, "sync: " + i + " for " + authorityInfo.base + "." + Log.v(TAG, "sync: " + i + " for " + authorityInfo.authority + "." + " period: " + (periodInMillis) + " flex: " + (flexInMillis) + " remaining: " + (remainingMillis) @@ -2224,49 +2231,41 @@ 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 (remainingMillis == periodInMillis // Case 1 + if (runEarly // Case 4 + || remainingMillis == periodInMillis // Case 1 || lastPollTimeAbsolute > nowAbsolute // Case 2 - || timeSinceLastRunMillis >= periodInMillis // Case 3 - || runEarly) { // Case 4 + || timeSinceLastRunMillis >= periodInMillis) { // Case 3 // Sync now - SyncStorageEngine.EndPoint target = authorityInfo.base; - final Pair backoff = - mSyncStorageEngine.getBackoff(target); + + final Pair backoff = mSyncStorageEngine.getBackoff( + authorityInfo.account, authorityInfo.userId, + authorityInfo.authority); + final RegisteredServicesCache.ServiceInfo syncAdapterInfo; + syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey( + authorityInfo.authority, authorityInfo.account.type), + authorityInfo.userId); + if (syncAdapterInfo == null) { + continue; + } mSyncStorageEngine.setPeriodicSyncTime(authorityInfo.ident, authorityInfo.periodicSyncs.get(i), nowAbsolute); - - if (target.target_provider) { - final RegisteredServicesCache.ServiceInfo - 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))); - } + 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())); + } // Compute when this periodic sync should next run. long nextPollTimeAbsolute; @@ -2313,7 +2312,8 @@ 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. - if (mRunningAccounts == INITIAL_ACCOUNTS_ARRAY) { + AccountAndUser[] accounts = mRunningAccounts; + if (accounts == INITIAL_ACCOUNTS_ARRAY) { if (isLoggable) { Log.v(TAG, "maybeStartNextSync: accounts not known, skipping"); } @@ -2323,6 +2323,9 @@ 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 @@ -2344,23 +2347,40 @@ public class SyncManager { while (operationIterator.hasNext()) { final SyncOperation op = operationIterator.next(); - // 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 all sync operations for + " - + op.target.userId + ": user not running."); - } - continue; - } - if (!isOperationValidLocked(op)) { + // 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 (isLoggable) { + Log.v(TAG, " Dropping sync operation: account doesn't exist."); + } continue; } + + // Drop this sync request if it isn't syncable. + int syncableState = getIsSyncable( + op.account, op.userId, op.authority); + if (syncableState == 0) { + 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) { @@ -2368,16 +2388,42 @@ public class SyncManager { nextReadyToRunTime = op.effectiveRunTime; } if (isLoggable) { - Log.v(TAG, " Not running sync operation: Sync too far in future." - + "effective: " + op.effectiveRunTime + " flex: " + op.flexTime - + " now: " + now); + Log.v(TAG, " Dropping sync operation: Sync too far in future."); } continue; } - // Add this sync to be run. + // TODO: change this behaviour for non-registered syncs. + final RegisteredServicesCache.ServiceInfo 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; + } + operations.add(op); } - for (Integer user : removedUsers) { // if it's still removed if (mUserManager.getUserInfo(user) == null) { @@ -2419,9 +2465,13 @@ public class SyncManager { } } } - if (activeOp.isConflict(candidate)) { + 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))) { 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) { @@ -2471,8 +2521,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 @@ -2501,113 +2551,7 @@ 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 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)) { @@ -2617,48 +2561,27 @@ public class SyncManager { Log.v(TAG, syncContext.toString()); } } - // 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 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; - } + + // connect to the sync adapter + SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type); + final RegisteredServicesCache.ServiceInfo 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; } + ActiveSyncContext activeSyncContext = - new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid); + new ActiveSyncContext(op, insertStartSyncEvent(op), syncAdapterInfo.uid); activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext); mActiveSyncContexts.add(activeSyncContext); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext); } - if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) { - Log.e(TAG, "Bind attempt failed - target: " + targetComponent); + if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) { + Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo); closeActiveSyncContext(activeSyncContext); return false; } @@ -2666,54 +2589,47 @@ public class SyncManager { return true; } - private void runBoundToAdapter(final ActiveSyncContext activeSyncContext, - IBinder syncAdapter, int target) { + private void runBoundToSyncAdapter(final ActiveSyncContext activeSyncContext, + ISyncAdapter syncAdapter) { + activeSyncContext.mSyncAdapter = syncAdapter; final SyncOperation syncOperation = activeSyncContext.mSyncOperation; try { activeSyncContext.mIsLinkedToDeath = true; - syncAdapter.linkToDeath(activeSyncContext, 0); + syncAdapter.asBinder().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); - } + syncAdapter.startSync(activeSyncContext, syncOperation.authority, + syncOperation.account, syncOperation.extras); } catch (RemoteException remoteExc) { Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc); closeActiveSyncContext(activeSyncContext); increaseBackoffSetting(syncOperation); - scheduleSyncOperation( - new SyncOperation(syncOperation, 0L /* newRunTimeFromNow */)); + scheduleSyncOperation(new SyncOperation(syncOperation)); } catch (RuntimeException exc) { closeActiveSyncContext(activeSyncContext); Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc); } } - /** - * 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) { + private void cancelActiveSyncLocked(Account account, int userId, String authority) { ArrayList activeSyncs = new ArrayList(mActiveSyncContexts); for (ActiveSyncContext activeSyncContext : activeSyncs) { if (activeSyncContext != null) { - final SyncStorageEngine.EndPoint opInfo = - activeSyncContext.mSyncOperation.target; - if (!opInfo.matches(info)) { - continue; + // if an account was specified then only cancel the sync if it matches + if (account != null) { + if (!account.equals(activeSyncContext.mSyncOperation.account)) { + continue; + } } - if (extras != null && - !syncExtrasEquals(activeSyncContext.mSyncOperation.extras, - extras, - false /* no config settings */)) { + // if an authority was specified then only cancel the sync if it matches + if (authority != null) { + if (!authority.equals(activeSyncContext.mSyncOperation.authority)) { + continue; + } + } + // check if the userid matches + if (userId != UserHandle.USER_ALL + && userId != activeSyncContext.mSyncOperation.userId) { continue; } runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */, @@ -2726,19 +2642,16 @@ 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) { - if (info.target_provider) { - activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); - } else { - activeSyncContext.mSyncServiceAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); - } + activeSyncContext.mSyncAdapter.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; @@ -2780,12 +2693,6 @@ 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; @@ -2795,35 +2702,24 @@ public class SyncManager { stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage, upstreamActivity, downstreamActivity, elapsedTime); - // 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)); - } + if (syncResult != null && syncResult.tooManyDeletions) { + installHandleTooManyDeletesNotification(syncOperation.account, + syncOperation.authority, syncResult.stats.numDeletes, + syncOperation.userId); } else { - 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)); - } + 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)); } // no need to schedule an alarm, as that will be done by our caller. } @@ -2832,7 +2728,7 @@ public class SyncManager { activeSyncContext.close(); mActiveSyncContexts.remove(activeSyncContext); mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo, - activeSyncContext.mSyncOperation.target.userId); + activeSyncContext.mSyncOperation.userId); } /** @@ -3094,16 +2990,26 @@ public class SyncManager { } public long insertStartSyncEvent(SyncOperation syncOperation) { + final int source = syncOperation.syncSource; final long now = System.currentTimeMillis(); - EventLog.writeEvent(2720, - syncOperation.toEventLog(SyncStorageEngine.EVENT_START)); - return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now); + + 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 + ); } public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, int upstreamActivity, int downstreamActivity, long elapsedTime) { - EventLog.writeEvent(2720, - syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP)); + EventLog.writeEvent(2720, syncOperation.authority, + SyncStorageEngine.EVENT_STOP, syncOperation.syncSource, + syncOperation.account.name.hashCode()); + mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime, resultMessage, downstreamActivity, upstreamActivity); } @@ -3118,79 +3024,6 @@ 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 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 eef20b27f30f2..ce1dde4aad794 100644 --- a/services/java/com/android/server/content/SyncOperation.java +++ b/services/java/com/android/server/content/SyncOperation.java @@ -20,9 +20,10 @@ 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.Log; +import android.util.Pair; /** * Value type that represents a sync operation. @@ -31,13 +32,10 @@ import android.util.Log; * {@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; @@ -56,21 +54,19 @@ public class SyncOperation implements Comparable { "UserStart", }; - 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} */ + /** 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 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; @@ -83,56 +79,25 @@ 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 provider, + public SyncOperation(Account account, int userId, int reason, int source, String authority, Bundle extras, long runTimeFromNow, long flexTime, long backoff, long delayUntil, boolean allowParallelSyncs) { - this.target = new SyncStorageEngine.EndPoint(account, provider, userId); + this.service = null; + this.account = account; + this.authority = authority; + this.userId = userId; this.reason = reason; - 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.allowParallelSyncs = allowParallelSyncs; 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; @@ -143,11 +108,41 @@ public class SyncOperation implements Comparable { this.flexTime = flexTime; } updateEffectiveRunTime(); - return toKey(info, extras); + this.key = toKey(); } - public boolean matchesAuthority(SyncOperation other) { - return this.target.matches(other.target); + public SyncOperation(SyncRequest request, int userId, int reason, int source, long backoff, + long delayUntil, boolean allowParallelSyncs) { + if (request.hasAuthority()) { + Pair 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(); } /** @@ -164,7 +159,11 @@ 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_DISALLOW_METERED); + removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_ALLOW_METERED); + + // Remove Config data. + bundle.remove(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD); + bundle.remove(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD); } private void removeFalseExtra(Bundle bundle, String extraName) { @@ -173,24 +172,22 @@ public class SyncOperation implements Comparable { } } - /** - * 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; - } + /** 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(); } @Override @@ -199,26 +196,18 @@ public class SyncOperation implements Comparable { } public String dump(PackageManager pm, boolean useOneLine) { - StringBuilder sb = new StringBuilder(); - if (target.target_provider) { - sb.append(target.account.name) + StringBuilder sb = new StringBuilder() + .append(account.name) .append(" u") - .append(target.userId).append(" (") - .append(target.account.type) + .append(userId).append(" (") + .append(account.type) .append(")") .append(", ") - .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); + .append(authority) + .append(", ") + .append(SyncStorageEngine.SOURCES[syncSource]) + .append(", latestRunTime ") + .append(latestRunTime); if (expedited) { sb.append(", EXPEDITED"); } @@ -256,6 +245,10 @@ 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); } @@ -268,39 +261,28 @@ 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. */ - public static String toKey(SyncStorageEngine.EndPoint info, Bundle extras) { + private String toKey() { StringBuilder sb = new StringBuilder(); - if (info.target_provider) { - sb.append("provider: ").append(info.provider); - sb.append(" account {name=" + info.account.name - + ", user=" - + info.userId - + ", type=" - + info.account.type + if (service == null) { + sb.append("authority: ").append(authority); + sb.append(" account {name=" + account.name + ", user=" + userId + ", type=" + account.type + "}"); - } else if (info.target_service) { - sb.append("service {package=" ) - .append(info.service.getPackageName()) - .append(" user=") - .append(info.userId) - .append(", class=") - .append(info.service.getClassName()) - .append("}"); } else { - Log.v(TAG, "Converting SyncOperaton to key, invalid target: " + info.toString()); - return ""; + sb.append("service {package=" ) + .append(service.getPackageName()) + .append(" user=") + .append(userId) + .append(", class=") + .append(service.getClassName()) + .append("}"); } sb.append(" extras: "); extrasToStringBuilder(extras, sb); return sb.toString(); } - private static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) { + public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) { sb.append("["); for (String key : bundle.keySet()) { sb.append(key).append("=").append(bundle.get(key)).append(" "); @@ -308,31 +290,6 @@ 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 @@ -368,21 +325,4 @@ 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 da7efba56ab41..6f3fe6e1d37bc 100644 --- a/services/java/com/android/server/content/SyncQueue.java +++ b/services/java/com/android/server/content/SyncQueue.java @@ -16,11 +16,12 @@ 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; @@ -59,51 +60,25 @@ public class SyncQueue { public void addPendingOperations(int userId) { for (SyncStorageEngine.PendingOperation op : mSyncStorageEngine.getPendingOperations()) { - final SyncStorageEngine.EndPoint info = op.authority; - if (info.userId != userId) continue; + if (op.userId != userId) continue; - final Pair backoff = mSyncStorageEngine.getBackoff(info); - SyncOperation operationToAdd; - if (info.target_provider) { - final ServiceInfo 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); + final Pair backoff = mSyncStorageEngine.getBackoff( + op.account, op.userId, op.authority); + final ServiceInfo 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; } + 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); } } @@ -144,8 +119,12 @@ 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 = mSyncStorageEngine.insertIntoPending(operation); + pop = new SyncStorageEngine.PendingOperation( + operation.account, operation.userId, operation.reason, operation.syncSource, + operation.authority, operation.extras, operation.expedited); + pop = mSyncStorageEngine.insertIntoPending(pop); if (pop == null) { throw new IllegalStateException("error adding pending sync operation " + operation); @@ -157,16 +136,17 @@ public class SyncQueue { return true; } - public void removeUserLocked(int userId) { + public void removeUser(int userId) { ArrayList opsToRemove = new ArrayList(); for (SyncOperation op : mOperationsMap.values()) { - if (op.target.userId == userId) { + if (op.userId == userId) { opsToRemove.add(op); } } - for (SyncOperation op : opsToRemove) { - remove(op); - } + + for (SyncOperation op : opsToRemove) { + remove(op); + } } /** @@ -174,15 +154,8 @@ 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)) { @@ -191,58 +164,41 @@ public class SyncQueue { } } - /** 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 + public void onBackoffChanged(Account account, int userId, String providerName, long backoff) { + // for each op that matches the account and provider update its // backoff and effectiveStartTime for (SyncOperation op : mOperationsMap.values()) { - if (op.target.matches(target)) { + if (op.account.equals(account) && op.authority.equals(providerName) + && op.userId == userId) { op.backoff = backoff; op.updateEffectiveRunTime(); } } } - public void onDelayUntilTimeChanged(SyncStorageEngine.EndPoint target, long delayUntil) { - // for each op that matches the authority info of the provided op, change the delay time. + public void onDelayUntilTimeChanged(Account account, String providerName, long delayUntil) { + // for each op that matches the account and provider update its + // delayUntilTime and effectiveStartTime for (SyncOperation op : mOperationsMap.values()) { - if (op.target.matches(target)) { + if (op.account.equals(account) && op.authority.equals(providerName)) { op.delayUntil = delayUntil; op.updateEffectiveRunTime(); } } } - /** - * 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) { + public void remove(Account account, int userId, String authority) { Iterator> entries = mOperationsMap.entrySet().iterator(); while (entries.hasNext()) { Map.Entry entry = entries.next(); SyncOperation syncOperation = entry.getValue(); - final SyncStorageEngine.EndPoint opInfo = syncOperation.target; - if (!opInfo.matches(info)) { + if (account != null && !syncOperation.account.equals(account)) { continue; } - if (extras != null - && !SyncManager.syncExtrasEquals( - syncOperation.extras, - extras, - false /* no config flags*/)) { + if (authority != null && !syncOperation.authority.equals(authority)) { + continue; + } + if (userId != syncOperation.userId) { continue; } entries.remove(); diff --git a/services/java/com/android/server/content/SyncStorageEngine.java b/services/java/com/android/server/content/SyncStorageEngine.java index aafcd70d8d963..25529a69f4ef7 100644 --- a/services/java/com/android/server/content/SyncStorageEngine.java +++ b/services/java/com/android/server/content/SyncStorageEngine.java @@ -24,7 +24,6 @@ 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; @@ -37,12 +36,10 @@ 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; @@ -110,7 +107,10 @@ 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. */ @@ -118,9 +118,6 @@ 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; @@ -130,8 +127,7 @@ public class SyncStorageEngine extends Handler { "LOCAL", "POLL", "USER", - "PERIODIC", - "SERVICE"}; + "PERIODIC" }; // The MESG column will contain one of these or one of the Error types. public static final String MESG_SUCCESS = "success"; @@ -159,53 +155,41 @@ public class SyncStorageEngine extends Handler { } public static class PendingOperation { - final EndPoint authority; + final Account account; + final int userId; final int reason; final int syncSource; + final String authority; final Bundle extras; // note: read-only. + final ComponentName serviceName; final boolean expedited; - final int authorityId; - // No longer used. - // Keep around for sake up updating from pending.bin to pending.xml + int authorityId; byte[] flatExtras; - PendingOperation(AuthorityInfo authority, int reason, int source, - Bundle extras, boolean expedited) { - this.authority = authority.base; + PendingOperation(Account account, int userId, int reason, int source, + String authority, Bundle extras, boolean expedited) { + this.account = account; + this.userId = userId; this.syncSource = source; this.reason = reason; + this.authority = authority; this.extras = extras != null ? new Bundle(extras) : extras; this.expedited = expedited; - this.authorityId = authority.ident; + this.authorityId = -1; + this.serviceName = null; } 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; - } - - /** - * 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; + this.serviceName = other.serviceName; } } @@ -219,93 +203,17 @@ public class SyncStorageEngine extends Handler { } } - /** 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); + public static class AuthorityInfo { final ComponentName service; final Account account; final int userId; - 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 String authority; 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 periodicSyncs; /** @@ -315,7 +223,10 @@ public class SyncStorageEngine extends Handler { * @param toCopy AuthorityInfo to be copied. */ AuthorityInfo(AuthorityInfo toCopy) { - base = toCopy.base; + account = toCopy.account; + userId = toCopy.userId; + authority = toCopy.authority; + service = toCopy.service; ident = toCopy.ident; enabled = toCopy.enabled; syncable = toCopy.syncable; @@ -329,42 +240,56 @@ public class SyncStorageEngine extends Handler { } } - 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(); - defaultInitialisation(); - } - - private void defaultInitialisation() { + /** + * 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; 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 - 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); - } - + periodicSyncs = new ArrayList(); + // 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))); } /** - * Two AuthorityInfos are considered equal if they have the same authority. + * 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. */ - public boolean equals(EndPoint other) { - return base.matches(other); + 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(); + periodicSyncs.add(new PeriodicSync(account, authority, + new Bundle(), + DEFAULT_POLL_FREQUENCY_SECONDS, + calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS))); } } @@ -396,9 +321,16 @@ public class SyncStorageEngine extends Handler { } interface OnSyncRequestListener { - - /** 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); + /** + * 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); } // Primary list of all syncable authorities. Also our global lock. @@ -424,8 +356,8 @@ public class SyncStorageEngine extends Handler { = new RemoteCallbackList(); /** Reverse mapping for component name -> authority id>. */ - private final ArrayMap> mServices = - new ArrayMap>(); + private final HashMap> mServices = + new HashMap>(); private int mNextAuthorityId = 0; @@ -488,12 +420,9 @@ public class SyncStorageEngine extends Handler { File systemDir = new File(dataDir, "system"); File syncDir = new File(systemDir, "sync"); syncDir.mkdirs(); - - maybeDeleteLegacyPendingInfoLocked(syncDir); - mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); mStatusFile = new AtomicFile(new File(syncDir, "status.bin")); - mPendingFile = new AtomicFile(new File(syncDir, "pending.xml")); + mPendingFile = new AtomicFile(new File(syncDir, "pending.bin")); mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin")); readAccountInfoLocked(); @@ -507,23 +436,6 @@ public class SyncStorageEngine extends Handler { writeStatisticsLocked(); } - /** - * Ensure the old pending.bin is deleted, as it has been changed to pending.xml. This was due - * to changes to the wire format for parceling bundles in KLP which make them incompatible with - * previous versions. Because of this incompatibility we don't try to keep them - instead we - * throw them out. - * pending.bin was used up to KLP, and after that we use pending.xml. - * @param syncDir directory where the sync files are located. - */ - private void maybeDeleteLegacyPendingInfoLocked(File syncDir) { - File file = new File(syncDir, "pending.bin"); - if (!file.exists()) { - return; - } else { - file.delete(); - } - } - public static SyncStorageEngine newTestInstance(Context context) { return new SyncStorageEngine(context, context.getFilesDir()); } @@ -639,8 +551,7 @@ public class SyncStorageEngine extends Handler { public boolean getSyncAutomatically(Account account, int userId, String providerName) { synchronized (mAuthorities) { if (account != null) { - AuthorityInfo authority = getAuthorityLocked( - new EndPoint(account, providerName, userId), + AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, "getSyncAutomatically"); return authority != null && authority.enabled; } @@ -648,9 +559,10 @@ public class SyncStorageEngine extends Handler { int i = mAuthorities.size(); while (i > 0) { i--; - AuthorityInfo authorityInfo = mAuthorities.valueAt(i); - if (authorityInfo.base.matches(new EndPoint(account, providerName, userId)) - && authorityInfo.enabled) { + AuthorityInfo authority = mAuthorities.valueAt(i); + if (authority.authority.equals(providerName) + && authority.userId == userId + && authority.enabled) { return true; } } @@ -665,11 +577,8 @@ public class SyncStorageEngine extends Handler { + ", user " + userId + " -> " + sync); } synchronized (mAuthorities) { - AuthorityInfo authority = - getOrCreateAuthorityLocked( - new EndPoint(account, providerName, userId), - -1 /* ident */, - false); + AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1, + false); if (authority.enabled == sync) { if (DEBUG) { Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing"); @@ -690,9 +599,8 @@ public class SyncStorageEngine extends Handler { public int getIsSyncable(Account account, int userId, String providerName) { synchronized (mAuthorities) { if (account != null) { - AuthorityInfo authority = getAuthorityLocked( - new EndPoint(account, providerName, userId), - "get authority syncable"); + AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, + "getIsSyncable"); if (authority == null) { return -1; } @@ -702,10 +610,9 @@ public class SyncStorageEngine extends Handler { int i = mAuthorities.size(); while (i > 0) { i--; - AuthorityInfo authorityInfo = mAuthorities.valueAt(i); - if (authorityInfo.base != null - && authorityInfo.base.provider.equals(providerName)) { - return authorityInfo.syncable; + AuthorityInfo authority = mAuthorities.valueAt(i); + if (authority.authority.equals(providerName)) { + return authority.syncable; } } return -1; @@ -713,159 +620,102 @@ 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: " + aInfo.toString() + " -> " + syncable); - - if (aInfo.syncable == syncable) { - if (DEBUG) Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing"); - return; + if (DEBUG) { + Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + + ", user " + userId + " -> " + 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; + } + authority.syncable = syncable; + writeAccountInfoLocked(); } - aInfo.syncable = syncable; - writeAccountInfoLocked(); if (syncable > 0) { - requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle()); + requestSync(account, userId, SyncOperation.REASON_IS_SYNCABLE, providerName, + new Bundle()); } reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } - public Pair getBackoff(EndPoint info) { + public Pair getBackoff(Account account, int userId, String providerName) { synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(info, "getBackoff"); - if (authority != null) { - return Pair.create(authority.backoffTime, authority.backoffDelay); + AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, + "getBackoff"); + if (authority == null || authority.backoffTime < 0) { + return null; } - return null; + return Pair.create(authority.backoffTime, authority.backoffDelay); } } - /** - * 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) { + public void setBackoff(Account account, int userId, String providerName, + long nextSyncTime, long nextDelay) { if (DEBUG) { - Log.v(TAG, "setBackoff: " + info + Log.v(TAG, "setBackoff: " + account + ", provider " + providerName + + ", user " + userId + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay); } - boolean changed; + boolean changed = false; synchronized (mAuthorities) { - 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 authorityInfo = - getOrCreateAuthorityLocked(info, -1 /* ident */, true); - if (authorityInfo.backoffTime == nextSyncTime - && authorityInfo.backoffDelay == nextDelay) { - changed = false; - } else { - authorityInfo.backoffTime = nextSyncTime; - authorityInfo.backoffDelay = nextDelay; - changed = true; + 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; + } + } } + } else { + AuthorityInfo authority = + getOrCreateAuthorityLocked(account, userId, providerName, -1 /* ident */, + true); + if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) { + return; + } + 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) { Log.v(TAG, "clearAllBackoffs:" - + " authority:" + authorityInfo.base + + " authority:" + authorityInfo.authority + " account:" + accountInfo.accountAndUser.account.name + " user:" + accountInfo.accountAndUser.userId + " backoffTime was: " + authorityInfo.backoffTime @@ -873,23 +723,12 @@ 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 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(); } } @@ -898,9 +737,28 @@ public class SyncStorageEngine extends Handler { } } - public long getDelayUntilTime(EndPoint info) { + public void setDelayUntilTime(Account account, int userId, String providerName, + long delayUntil) { + if (DEBUG) { + Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName + + ", user " + userId + " -> delayUntil " + delayUntil); + } synchronized (mAuthorities) { - AuthorityInfo authority = getAuthorityLocked(info, "getDelayUntil"); + AuthorityInfo authority = getOrCreateAuthorityLocked( + account, userId, providerName, -1 /* ident */, true); + if (authority.delayUntil == delayUntil) { + return; + } + authority.delayUntil = delayUntil; + } + + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + } + + public long getDelayUntilTime(Account account, int userId, String providerName) { + synchronized (mAuthorities) { + AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, + "getDelayUntil"); if (authority == null) { return 0; } @@ -908,148 +766,110 @@ public class SyncStorageEngine extends Handler { } } - public void setDelayUntilTime(EndPoint info, long delayUntil) { + private void updateOrRemovePeriodicSync(PeriodicSync toUpdate, int userId, boolean add) { if (DEBUG) { - Log.v(TAG, "setDelayUntil: " + info - + " -> delayUntil " + delayUntil); + Log.v(TAG, "addOrRemovePeriodicSync: " + toUpdate.account + ", user " + userId + + ", provider " + toUpdate.authority + + " -> period " + toUpdate.period + ", extras " + toUpdate.extras); } synchronized (mAuthorities) { - AuthorityInfo authority = getOrCreateAuthorityLocked(info, -1, true); - if (authority.delayUntil == delayUntil) { - return; + if (toUpdate.period <= 0 && add) { + Log.e(TAG, "period < 0, should never happen in updateOrRemovePeriodicSync: add-" + add); } - authority.delayUntil = delayUntil; - } - reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); - } - - public void updateOrAddPeriodicSync(EndPoint info, long period, long flextime, Bundle extras) { - if (DEBUG) { - Log.v(TAG, "addPeriodicSync: " + info - + " -> period " + period + ", flex " + flextime + ", extras " - + extras.toString()); - } - synchronized (mAuthorities) { - if (period <= 0) { - Log.e(TAG, "period < 0, should never happen in updateOrAddPeriodicSync"); - } - if (extras == null) { - Log.e(TAG, "null extras, should never happen in updateOrAddPeriodicSync:"); + if (toUpdate.extras == null) { + Log.e(TAG, "period < 0, should never happen in updateOrRemovePeriodicSync: add-" + add); } try { - PeriodicSync toUpdate; - if (info.target_provider) { - toUpdate = new PeriodicSync(info.account, - info.provider, - 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; + } + } + // 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, 0); + } } else { - toUpdate = new PeriodicSync(info.service, - extras, - period, - flextime); - } - AuthorityInfo authority = - 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(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 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); + // Remove any periodic syncs that match the authority and extras. + SyncStatusInfo status = mSyncStatus.get(authority.ident); + boolean changed = false; + Iterator 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."); + } } else { - Log.e(TAG, "Tried removing sync status on remove periodic sync but" - + " did not find it."); + i++; } - } else { - i++; } - } - if (!changed) { - return; + if (!changed) { + return; + } } } finally { writeAccountInfoLocked(); writeStatusLocked(); } } + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); } - /** - * @return list of periodic syncs for an authority. Never returns null - if no such syncs - * exist, returns an empty list. - */ - public List getPeriodicSyncs(EndPoint info) { + 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 getPeriodicSyncs(Account account, int userId, String providerName) { + ArrayList syncs = new ArrayList(); synchronized (mAuthorities) { - AuthorityInfo authorityInfo = getAuthorityLocked(info, "getPeriodicSyncs"); - ArrayList syncs = new ArrayList(); - if (authorityInfo != null) { - for (PeriodicSync item : authorityInfo.periodicSyncs) { + AuthorityInfo authority = getAuthorityLocked(account, userId, providerName, + "getPeriodicSyncs"); + if (authority != null) { + for (PeriodicSync item : authority.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 && auto == flag) { + if (auto != null && (boolean) auto == flag) { return; } mMasterSyncAutomatically.put(userId, flag); @@ -1070,6 +890,12 @@ 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); @@ -1077,47 +903,53 @@ public class SyncStorageEngine extends Handler { } /** - * Returns true if there is currently a sync operation being actively processed for the given - * authority. + * Returns true if there is currently a sync operation for the given + * account or authority actively being processed. */ - public boolean isSyncActive(EndPoint info) { + public boolean isSyncActive(Account account, int userId, String authority) { synchronized (mAuthorities) { - for (SyncInfo syncInfo : getCurrentSyncs(info.userId)) { + for (SyncInfo syncInfo : getCurrentSyncs(userId)) { AuthorityInfo ainfo = getAuthority(syncInfo.authorityId); - if (ainfo != null && ainfo.base.matches(info)) { + if (ainfo != null && ainfo.account.equals(account) + && ainfo.authority.equals(authority) + && ainfo.userId == userId) { return true; } } } + return false; } - public PendingOperation insertIntoPending(SyncOperation op) { - PendingOperation pop; + public PendingOperation insertIntoPending(PendingOperation op) { synchronized (mAuthorities) { if (DEBUG) { - Log.v(TAG, "insertIntoPending: authority=" + op.target + Log.v(TAG, "insertIntoPending: account=" + op.account + + " user=" + op.userId + + " auth=" + op.authority + + " src=" + op.syncSource + " extras=" + op.extras); } - final EndPoint info = op.target; - AuthorityInfo authority = - getOrCreateAuthorityLocked(info, - -1 /* desired identifier */, - true /* write accounts to storage */); + + AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, op.userId, + op.authority, + -1 /* desired identifier */, + true /* write accounts to storage */); if (authority == null) { return null; } - pop = new PendingOperation(authority, op.reason, op.syncSource, op.extras, - op.expedited); - mPendingOperations.add(pop); + op = new PendingOperation(op); + op.authorityId = authority.ident; + mPendingOperations.add(op); writePendingOperationsLocked(); SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); status.pending = true; } + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); - return pop; + return op; } /** @@ -1125,13 +957,18 @@ 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 Pending op to delete. + * @param op + * @return */ public boolean deleteFromPending(PendingOperation op) { boolean res = false; synchronized (mAuthorities) { if (DEBUG) { - Log.v(TAG, "deleteFromPending: account=" + op.toString()); + Log.v(TAG, "deleteFromPending: account=" + op.account + + " user=" + op.userId + + " auth=" + op.authority + + " src=" + op.syncSource + + " extras=" + op.extras); } if (mPendingOperations.remove(op)) { if (mPendingOperations.size() == 0 @@ -1141,14 +978,18 @@ public class SyncStorageEngine extends Handler { } else { mNumPendingFinished++; } - AuthorityInfo authority = getAuthorityLocked(op.authority, "deleteFromPending"); + + AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority, + "deleteFromPending"); if (authority != null) { if (DEBUG) 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 MAX_HISTORY) { @@ -1477,9 +1317,10 @@ public class SyncStorageEngine extends Handler { /** * Return a copy of the specified authority with the corresponding sync status */ - public Pair getCopyOfAuthorityWithSyncStatus(EndPoint info) { + public Pair getCopyOfAuthorityWithSyncStatus( + Account account, int userId, String authority) { synchronized (mAuthorities) { - AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(info, + AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(account, userId, authority, -1 /* assign a new identifier if creating a new authority */, true /* write to storage if this results in a change */); return createCopyPairOfAuthorityWithSyncStatusLocked(authorityInfo); @@ -1507,19 +1348,20 @@ public class SyncStorageEngine extends Handler { * @param authority the authority whose row should be selected * @return the SyncStatusInfo for the authority */ - public SyncStatusInfo getStatusByAuthority(EndPoint info) { - if (info.target_provider && (info.account == null || info.provider == null)) { + public SyncStatusInfo getStatusByAccountAndAuthority(Account account, int userId, + String authority) { + if (account == null || authority == 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 aInfo = mServices.get(info.service); - AuthorityInfo authority = null; - if (aInfo != null) { - authority = aInfo.get(info.userId); - } - if (authority == null) { - if (tag != null) { - if (DEBUG) { - Log.v(TAG, tag + " No authority info found for " + info.service + " for user " - + info.userId); - } + 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); } - return null; } - 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 (DEBUG) { - Log.v(TAG, tag + ": unknown account " + au); - } - } - return null; - } - AuthorityInfo authority = accountInfo.authorities.get(info.provider); - if (authority == null) { - if (tag != null) { - if (DEBUG) { - Log.v(TAG, tag + ": unknown provider " + info.provider); - } - } - return null; - } - return authority; - } else { - Log.e(TAG, tag + " Authority : " + info + ", invalid target"); return null; } + AuthorityInfo authority = accountInfo.authorities.get(authorityName); + if (authority == null) { + if (tag != null) { + if (DEBUG) { + Log.v(TAG, tag + ": unknown authority " + authorityName); + } + } + return null; + } + + return authority; } /** - * @param info info identifying 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); + } + } + return null; + } + return authority; + } + + /** + * @param cname identifier for the service. + * @param userId for the syncs corresponding to this 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 authority, creating it if none + * @return the authority that corresponds to the provided sync service, creating it if none * exists. */ - private AuthorityInfo getOrCreateAuthorityLocked(EndPoint info, int ident, boolean doWrite) { - AuthorityInfo authority = null; - if (info.target_service) { - SparseArray aInfo = mServices.get(info.service); - if (aInfo == null) { - aInfo = new SparseArray(); - mServices.put(info.service, aInfo); + private AuthorityInfo getOrCreateAuthorityLocked(ComponentName cname, int userId, int ident, + boolean doWrite) { + SparseArray aInfo = mServices.get(cname); + if (aInfo == null) { + aInfo = new SparseArray(); + mServices.put(cname, aInfo); + } + AuthorityInfo authority = aInfo.get(userId); + if (authority == null) { + if (ident < 0) { + ident = mNextAuthorityId; + mNextAuthorityId++; + doWrite = true; } - authority = aInfo.get(info.userId); - if (authority == null) { - authority = createAuthorityLocked(info, ident, doWrite); - aInfo.put(info.userId, authority); + if (DEBUG) { + Log.v(TAG, "created a new AuthorityInfo for " + cname.getPackageName() + + ", " + cname.getClassName() + + ", user: " + userId); } - } 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); + authority = new AuthorityInfo(cname, userId, ident); + aInfo.put(userId, authority); + mAuthorities.put(ident, authority); + if (doWrite) { + writeAccountInfoLocked(); } } return authority; } - private AuthorityInfo createAuthorityLocked(EndPoint info, int ident, boolean doWrite) { - AuthorityInfo authority; - if (ident < 0) { - ident = mNextAuthorityId; - mNextAuthorityId++; - doWrite = true; + 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); } - if (DEBUG) { - Log.v(TAG, "created a new AuthorityInfo for " + info); - } - authority = new AuthorityInfo(info, ident); - mAuthorities.put(ident, authority); - if (doWrite) { - writeAccountInfoLocked(); + 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(); + } } + return authority; } - public void removeAuthority(EndPoint info) { - synchronized (mAuthorities) { - if (info.target_provider) { - removeAuthorityLocked(info.account, info.userId, info.provider, true /* doWrite */); - } else { - SparseArray 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)); @@ -1741,7 +1583,8 @@ 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) { @@ -1757,7 +1600,7 @@ public class SyncStorageEngine extends Handler { } if (!found) { Log.w(TAG, "Ignoring setPeriodicSyncTime request for a sync that does not exist. " + - "Authority: " + authorityInfo.base); + "Authority: " + authorityInfo.authority); } } @@ -1910,10 +1753,10 @@ public class SyncStorageEngine extends Handler { ArrayList authoritiesToRemove = new ArrayList(); final int N = mAuthorities.size(); - for (int i = 0; i < N; i++) { + for (int i=0; i 0) { i--; AuthorityInfo authority = mAuthorities.valueAt(i); - if (authority.base.equals(provider)) { + if (authority.authority.equals(provider)) { authority.enabled = value == null || Boolean.parseBoolean(value); authority.syncable = 1; } @@ -2458,18 +2274,18 @@ public class SyncStorageEngine extends Handler { } if (authority != null) { pop = new PendingOperation( - authority, reason, syncSource, new Bundle(), expedited); + authority.account, authority.userId, reason, syncSource, + authority.authority, new Bundle(), expedited); + pop.authorityId = authorityId; pop.flatExtras = null; // No longer used. mPendingOperations.add(pop); - if (DEBUG_FILE) { - Log.v(TAG, "Adding pending op: " - + pop.authority + if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + pop.account + + " auth=" + pop.authority + " src=" + pop.syncSource + " reason=" + pop.reason + " expedited=" + pop.expedited); - } } else { - // Skip non-existent authority. + // Skip non-existent authority; pop = null; if (DEBUG_FILE) { Log.v(TAG, "No authority found for " + authorityId @@ -2501,7 +2317,7 @@ public class SyncStorageEngine extends Handler { } } /** - * Old format of reading pending.bin as a parcelled file. Replaced in lieu of xml because + * Old format of reading pending.bin as a parcelled file. Replaced in lieu of JSON because * persisting parcels is unsafe. * @throws java.io.IOException */ @@ -2538,18 +2354,17 @@ public class SyncStorageEngine extends Handler { // to a regular sync by creating an empty extras extras = new Bundle(); } - PendingOperation op = - new PendingOperation(authority, reason, syncSource, extras, expedited); + PendingOperation op = new PendingOperation( + authority.account, authority.userId, reason, syncSource, + authority.authority, extras, expedited); + op.authorityId = authorityId; op.flatExtras = flatExtras; - if (DEBUG_FILE) { - Log.v(TAG, "Adding pending op: " - + op.authority - + " auth=" + op.authority - + " src=" + op.syncSource - + " reason=" + op.reason - + " expedited=" + op.expedited - + " extras=" + op.extras); - } + if (DEBUG_FILE) Log.v(TAG, "Adding pending op: account=" + op.account + + " auth=" + op.authority + + " src=" + op.syncSource + + " reason=" + op.reason + + " expedited=" + op.expedited + + " extras=" + op.extras); mPendingOperations.add(op); } } @@ -2589,8 +2404,8 @@ public class SyncStorageEngine extends Handler { private static final String XML_ATTR_EXPEDITED = "expedited"; private static final String XML_ATTR_REASON = "reason"; /** - * 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. + * 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(); @@ -2664,25 +2479,34 @@ 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()); - } - } +// /** +// * Update the pending ops file, if e +// */ +// private void appendPendingOperationLocked(PendingOperation op) { +// if (DEBUG_FILE) Log.v(TAG, "Appending to " + mPendingFile.getBaseFile()); +// FileOutputStream fos = null; +// try { +// fos = mPendingFile.openAppend(); +// } catch (java.io.IOException e) { +// if (DEBUG_FILE) Log.v(TAG, "Failed append; writing full file"); +// writePendingOperationsLocked(); +// return; +// } +// +// try { +// Parcel out = Parcel.obtain(); +// writePendingOperationLocked(op, out); +// fos.write(out.marshall()); +// out.recycle(); +// } catch (java.io.IOException e1) { +// Log.w(TAG, "Error writing pending operations", e1); +// } finally { +// try { +// fos.close(); +// } catch (java.io.IOException e2) { +// } +// } +// } private void requestSync(Account account, int userId, int reason, String authority, Bundle extras) { @@ -2692,10 +2516,7 @@ 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( - new EndPoint(account, authority, userId), - reason, - extras); + mSyncRequestListener.onSyncRequest(account, userId, reason, authority, extras); } else { ContentResolver.requestSync(account, authority, extras); } @@ -2789,8 +2610,10 @@ 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("(info: " + pop.authority.toString()) - .append(", extras: " + pop.extras) + sb.append("(" + pop.account) + .append(", " + pop.userId) + .append(", " + pop.authority) + .append(", " + 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 68622550595a4..dff6661f288d2 100644 --- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java +++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java @@ -17,7 +17,6 @@ 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; @@ -42,16 +41,9 @@ import java.util.List; 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; @@ -63,7 +55,6 @@ 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); @@ -268,6 +259,11 @@ public class SyncStorageEngineTest extends AndroidTestCase { 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())); + removePeriodicSyncs(engine, account1, 0, authority1); removePeriodicSyncs(engine, account2, 0, authority1); removePeriodicSyncs(engine, account1, 0, authority2); @@ -321,41 +317,6 @@ 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 = ("\n" - + "\n" - + "" - + "\n" - + "\n" - + "").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 syncs = engine.getPeriodicSyncs(syncService1, 0); - assertEquals(1, syncs.size()); - assertEquals(1, engine.getIsEnabled(syncService1, 0)); - } - - @SmallTest - public void testComponentSettings() throws Exception { - PeriodicSync sync1 = new PeriodicSync(syncService1, Bundle.EMPTY, dayPoll, dayFuzz); - engine.addPeriodicSync(sync1, 0); - - engine.set - } - @MediumTest /** * V2 introduces flex time as well as service components. @@ -367,12 +328,20 @@ public class SyncStorageEngineTest extends AndroidTestCase { final String authority2 = "auth2"; final String authority3 = "auth3"; - 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); + 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()); byte[] accountsFileData = ("\n" + "\n" @@ -398,7 +367,7 @@ public class SyncStorageEngineTest extends AndroidTestCase { fos.write(accountsFileData); accountInfoFile.finishWrite(fos); - engine.clearAndReadState(); + SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext); List syncs = engine.getPeriodicSyncs(account, 0, authority1); assertEquals("Got incorrect # of syncs", 1, syncs.size());