diff options
author | Matthew Williams <mjwilliams@google.com> | 2013-07-26 12:56:39 -0700 |
---|---|---|
committer | Matthew Williams <mjwilliams@google.com> | 2013-09-05 15:58:24 -0700 |
commit | 56dbf8f23677d28615e61ef2fbb0e738cca02528 (patch) | |
tree | 0b69d2fd85af218ef72ef39df30967519749aa1c /core | |
parent | c213bdea26019acccfc835b9ab8d63ba2130c7f9 (diff) | |
download | frameworks_base-56dbf8f23677d28615e61ef2fbb0e738cca02528.zip frameworks_base-56dbf8f23677d28615e61ef2fbb0e738cca02528.tar.gz frameworks_base-56dbf8f23677d28615e61ef2fbb0e738cca02528.tar.bz2 |
Fix broken javadocs
Change-Id: Ibf7f2ed92919efd36fffa963447b1a443c0bb9db
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/content/ContentResolver.java | 160 | ||||
-rw-r--r-- | core/java/android/content/IContentService.aidl | 64 | ||||
-rw-r--r-- | core/java/android/content/ISyncServiceAdapter.aidl (renamed from core/java/android/content/IAnonymousSyncAdapter.aidl) | 2 | ||||
-rw-r--r-- | core/java/android/content/PeriodicSync.java | 7 | ||||
-rw-r--r-- | core/java/android/content/SyncInfo.java | 23 | ||||
-rw-r--r-- | core/java/android/content/SyncRequest.java | 103 | ||||
-rw-r--r-- | core/java/android/content/SyncService.java | 117 |
7 files changed, 350 insertions, 126 deletions
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index a761a89..1fcf311 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -141,7 +141,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_ALLOW_METERED = "allow_metered"; + public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered"; /** * Set by the SyncManager to request that the SyncAdapter initialize itself for @@ -1744,12 +1744,25 @@ public abstract class ContentResolver { */ public static void cancelSync(Account account, String authority) { try { - getContentService().cancelSync(account, authority); + getContentService().cancelSync(account, authority, null); } 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 */ @@ -1815,6 +1828,10 @@ public abstract class ContentResolver { * * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}. + * <p>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 @@ -1832,13 +1849,7 @@ public abstract class ContentResolver { if (authority == null) { throw new IllegalArgumentException("authority must not be null"); } - 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)) { + if (invalidPeriodicExtras(extras)) { throw new IllegalArgumentException("illegal extras were set"); } try { @@ -1850,6 +1861,26 @@ public abstract class ContentResolver { } /** + * {@hide} + * Helper function to throw an <code>IllegalArgumentException</code> 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. * <p>This method requires the caller to hold the permission @@ -1875,19 +1906,28 @@ public abstract class ContentResolver { } /** - * Remove the specified sync. This will remove any syncs that have been scheduled to run, but - * will not cancel any running syncs. - * <p>This method requires the caller to hold the permission</p> - * 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 + * 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. + * <p>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. + *</p>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 * service/adapter, frequency, <b>and</b> extras bundle. * * @param request SyncRequest object containing information about sync to cancel. */ public static void cancelSync(SyncRequest request) { - // TODO: Finish this implementation. + 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 + } } /** @@ -1907,7 +1947,23 @@ public abstract class ContentResolver { throw new IllegalArgumentException("authority must not be null"); } try { - return getContentService().getPeriodicSyncs(account, authority); + 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. + * <p>The calling application must be signed with the same certificate as the target component, + * otherwise this call will fail. + */ + public static List<PeriodicSync> getPeriodicSyncs(ComponentName cname) { + if (cname == null) { + throw new IllegalArgumentException("Component must not be null"); + } + try { + return getContentService().getPeriodicSyncs(null, null, cname); } catch (RemoteException e) { throw new RuntimeException("the ContentService should always be reachable", e); } @@ -1943,6 +1999,38 @@ public abstract class ContentResolver { } /** + * Set whether the provided {@link SyncService} is available to process work. + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}. + * <p>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. + * <p>Set with {@link #setServiceActive(ComponentName cname, boolean active)}. + * <p>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. * <p>This method requires the caller to hold the permission @@ -1976,8 +2064,8 @@ public abstract class ContentResolver { } /** - * Returns true if there is currently a sync operation for the given - * account or authority in the pending list, or actively being processed. + * Returns true if there is currently a sync operation for the given account or authority + * actively being processed. * <p>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 @@ -1985,8 +2073,26 @@ 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); + 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); } catch (RemoteException e) { throw new RuntimeException("the ContentService should always be reachable", e); } @@ -2044,7 +2150,7 @@ public abstract class ContentResolver { */ public static SyncStatusInfo getSyncStatus(Account account, String authority) { try { - return getContentService().getSyncStatus(account, authority); + return getContentService().getSyncStatus(account, authority, null); } catch (RemoteException e) { throw new RuntimeException("the ContentService should always be reachable", e); } @@ -2060,7 +2166,15 @@ public abstract class ContentResolver { */ public static boolean isSyncPending(Account account, String authority) { try { - return getContentService().isSyncPending(account, authority); + 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); } catch (RemoteException e) { throw new RuntimeException("the ContentService should always be reachable", e); } diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl index 9ad5a19..73a76e8 100644 --- a/core/java/android/content/IContentService.aidl +++ b/core/java/android/content/IContentService.aidl @@ -17,6 +17,7 @@ package android.content; import android.accounts.Account; +import android.content.ComponentName; import android.content.SyncInfo; import android.content.ISyncStatusObserver; import android.content.SyncAdapterType; @@ -55,8 +56,14 @@ 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); + 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); /** * Check if the provider should be synced when a network tickle is received @@ -74,12 +81,14 @@ interface IContentService { void setSyncAutomatically(in Account account, String providerName, boolean sync); /** - * 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. + * 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. */ - List<PeriodicSync> getPeriodicSyncs(in Account account, String providerName); + List<PeriodicSync> getPeriodicSyncs(in Account account, String providerName, + in ComponentName cname); /** * Set whether or not the provider is to be synced on a periodic basis. @@ -112,15 +121,22 @@ interface IContentService { */ void setIsSyncable(in Account account, String providerName, int syncable); - void setMasterSyncAutomatically(boolean flag); - - boolean getMasterSyncAutomatically(); + /** + * Corresponds roughly to setIsSyncable(String account, String provider) for syncs that bind + * to a SyncService. + */ + void setServiceActive(in ComponentName cname, boolean active); /** - * Returns true if there is currently a sync operation for the given - * account or authority in the pending list, or actively being processed. + * 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 isSyncActive(in Account account, String authority); + boolean isServiceActive(in ComponentName cname); + + void setMasterSyncAutomatically(boolean flag); + + boolean getMasterSyncAutomatically(); List<SyncInfo> getCurrentSyncs(); @@ -131,17 +147,33 @@ 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 authority the authority whose row should be selected - * @return the SyncStatusInfo for the authority, or null if none exists + * @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. */ - SyncStatusInfo getSyncStatus(in Account account, String authority); + SyncStatusInfo getSyncStatus(in Account account, String authority, in ComponentName cname); /** * 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); + boolean isSyncPending(in Account account, String authority, in ComponentName cname); void addStatusChangeListener(int mask, ISyncStatusObserver callback); diff --git a/core/java/android/content/IAnonymousSyncAdapter.aidl b/core/java/android/content/ISyncServiceAdapter.aidl index a80cea3..d419307 100644 --- a/core/java/android/content/IAnonymousSyncAdapter.aidl +++ b/core/java/android/content/ISyncServiceAdapter.aidl @@ -24,7 +24,7 @@ import android.content.ISyncContext; * Provider specified). See {@link android.content.AbstractThreadedSyncAdapter}. * {@hide} */ -oneway interface IAnonymousSyncAdapter { +oneway interface ISyncServiceAdapter { /** * Initiate a sync. SyncAdapter-specific parameters may be specified in diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java index 6aca151..836c6f8 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 uses a service. */ + /** Whether this periodic sync runs on a {@link SyncService}. */ public final boolean isService; /** * How much flexibility can be taken in scheduling the sync, in seconds. @@ -64,8 +64,6 @@ public class PeriodicSync implements Parcelable { this.flexTime = 0L; } - // TODO: Add copy ctor from SyncRequest? - /** * Create a copy of a periodic sync. * {@hide} @@ -183,7 +181,8 @@ public class PeriodicSync implements Parcelable { } /** - * Periodic sync extra comparison function. + * Periodic sync extra comparison function. Duplicated from + * {@link com.android.server.content.SyncManager#syncExtrasEquals(Bundle b1, Bundle b2)} * {@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 0284882..61b11c2 100644 --- a/core/java/android/content/SyncInfo.java +++ b/core/java/android/content/SyncInfo.java @@ -17,6 +17,7 @@ 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; @@ -29,16 +30,24 @@ public class SyncInfo implements Parcelable { public final int authorityId; /** - * The {@link Account} that is currently being synced. + * The {@link Account} that is currently being synced. Will be null if this sync is running via + * a {@link SyncService}. */ public final Account account; /** - * The authority of the provider that is currently being synced. + * The authority of the provider that is currently being synced. Will be null if this sync + * is running via a {@link SyncService}. */ 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. * See {@link android.os.SystemClock#elapsedRealtime()}. @@ -46,12 +55,13 @@ public class SyncInfo implements Parcelable { public final long startTime; /** @hide */ - public SyncInfo(int authorityId, Account account, String authority, + public SyncInfo(int authorityId, Account account, String authority, ComponentName service, long startTime) { this.authorityId = authorityId; this.account = account; this.authority = authority; this.startTime = startTime; + this.service = service; } /** @hide */ @@ -62,17 +72,20 @@ public class SyncInfo implements Parcelable { /** @hide */ public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(authorityId); - account.writeToParcel(parcel, 0); + parcel.writeParcelable(account, flags); parcel.writeString(authority); parcel.writeLong(startTime); + parcel.writeParcelable(service, flags); + } /** @hide */ SyncInfo(Parcel parcel) { authorityId = parcel.readInt(); - account = new Account(parcel); + account = parcel.readParcelable(Account.class.getClassLoader()); 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 4474c70..201a8b3 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; - /** Allow this sync request on metered networks. */ - private final boolean mAllowMetered; + /** Don't allow this sync request on metered networks. */ + private final boolean mDisallowMetered; /** * Anticipated upload size in bytes. * TODO: Not yet used - we put this information into the bundle for simplicity. @@ -85,16 +85,32 @@ public class SyncRequest implements Parcelable { /** * {@hide} - * Throws a runtime IllegalArgumentException if this function is called for an - * anonymous sync. * - * @return (Account, Provider) for this SyncRequest. + * @return account object for this sync. + * @throws IllegalArgumentException if this function is called for a request that targets a + * sync service. */ - public Pair<Account, String> getProviderInfo() { + public Account getAccount() { if (!hasAuthority()) { - throw new IllegalArgumentException("Cannot getProviderInfo() for an anonymous sync."); + throw new IllegalArgumentException("Cannot getAccount() for a sync that targets a sync" + + "service."); } - return Pair.create(mAccountToSync, mAuthority); + 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; } /** @@ -159,7 +175,7 @@ public class SyncRequest implements Parcelable { parcel.writeLong(mSyncFlexTimeSecs); parcel.writeLong(mSyncRunTimeSecs); parcel.writeInt((mIsPeriodic ? 1 : 0)); - parcel.writeInt((mAllowMetered ? 1 : 0)); + parcel.writeInt((mDisallowMetered ? 1 : 0)); parcel.writeLong(mTxBytes); parcel.writeLong(mRxBytes); parcel.writeInt((mIsAuthority ? 1 : 0)); @@ -177,7 +193,7 @@ public class SyncRequest implements Parcelable { mSyncFlexTimeSecs = in.readLong(); mSyncRunTimeSecs = in.readLong(); mIsPeriodic = (in.readInt() != 0); - mAllowMetered = (in.readInt() != 0); + mDisallowMetered = (in.readInt() != 0); mTxBytes = in.readLong(); mRxBytes = in.readLong(); mIsAuthority = (in.readInt() != 0); @@ -207,7 +223,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); - mAllowMetered = b.mAllowMetered; + mDisallowMetered = b.mDisallowMetered; mTxBytes = b.mTxBytes; mRxBytes = b.mRxBytes; } @@ -253,7 +269,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 mAllowMetered; + private boolean mDisallowMetered; /** Priority of this sync relative to others from calling app [-2, 2]. Default 0. */ private int mPriority = 0; /** @@ -344,6 +360,10 @@ 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 <code>IllegalArgumentException</code> * will be thrown. + * <p>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. * @@ -407,10 +427,17 @@ public class SyncRequest implements Parcelable { } /** - * @param allow false to allow this transfer on metered networks. Default true. + * Will throw an <code>IllegalArgumentException</code> if called and + * {@link #setIgnoreSettings(boolean ignoreSettings)} has already been called. + * @param disallow true to allow this transfer on metered networks. Default false. + * */ - public Builder setAllowMetered(boolean allow) { - mAllowMetered = true; + public Builder setDisallowMetered(boolean disallow) { + if (mIgnoreSettings && disallow) { + throw new IllegalArgumentException("setDisallowMetered(true) after having" + + "specified that settings are ignored."); + } + mDisallowMetered = disallow; return this; } @@ -511,10 +538,17 @@ public class SyncRequest implements Parcelable { * * Not valid for periodic sync and will throw an <code>IllegalArgumentException</code> in * {@link #build()}. + * <p>Throws <code>IllegalArgumentException</code> if called and + * {@link #setDisallowMetered(boolean)} 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; } @@ -591,8 +625,8 @@ public class SyncRequest implements Parcelable { if (mIgnoreBackoff) { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); } - if (mAllowMetered) { - mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_ALLOW_METERED, true); + if (mDisallowMetered) { + mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, true); } if (mIgnoreSettings) { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true); @@ -604,18 +638,27 @@ public class SyncRequest implements Parcelable { mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); } if (mIsManual) { - mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true); + mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, 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. - validatePeriodicExtras(mCustomExtras); - validatePeriodicExtras(mSyncConfigExtras); + if (ContentResolver.invalidPeriodicExtras(mCustomExtras) || + ContentResolver.invalidPeriodicExtras(mSyncConfigExtras)) { + throw new IllegalArgumentException("Illegal extras were set"); + } } 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" @@ -623,23 +666,5 @@ public class SyncRequest implements Parcelable { } return new SyncRequest(this); } - - /** - * Helper function to throw an <code>IllegalArgumentException</code> 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 100fd40..3f99ce1 100644 --- a/core/java/android/content/SyncService.java +++ b/core/java/android/content/SyncService.java @@ -21,24 +21,28 @@ 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. * <p> - * 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 - * <b> must not </b> override the onBind() method unless you know what you're doing, - * which limits the usefulness of this service for other work. + * 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 <b> must not </b> override the onBind() method + * unless you know what you're doing, which limits the usefulness of this service for other work. + * <p>A {@link SyncService} can either be active or inactive. Different to an + * {@link AbstractThreadedSyncAdapter}, there is no + * {@link ContentResolver#setSyncAutomatically(android.accounts.Account account, String provider, boolean sync)}, + * as well as no concept of initialisation (you can handle your own if needed). * * <pre> - * <service ndroid:name=".MyAnonymousSyncService" android:permission="android.permission.SYNC" /> + * <service android:name=".MySyncService"/> * </pre> * Like @link android.content.AbstractThreadedSyncAdapter this service supports * multiple syncs at the same time. Each incoming startSync() with a unique tag @@ -48,41 +52,50 @@ import java.util.HashMap; * at once, so if you mutate local objects you must ensure synchronization. */ public abstract class SyncService extends Service { + private static final String TAG = "SyncService"; - /** SyncAdapter Instantiation that any anonymous syncs call. */ - private final AnonymousSyncAdapterImpl mSyncAdapter = new AnonymousSyncAdapterImpl(); + private final SyncAdapterImpl mSyncAdapter = new SyncAdapterImpl(); - /** Keep track of on-going syncs, keyed by tag. */ - @GuardedBy("mLock") - private final HashMap<Bundle, AnonymousSyncThread> - mSyncThreads = new HashMap<Bundle, AnonymousSyncThread>(); + /** Keep track of on-going syncs, keyed by bundle. */ + @GuardedBy("mSyncThreadLock") + private final SparseArray<SyncThread> + mSyncThreads = new SparseArray<SyncThread>(); /** 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; - @Override + /** {@hide} */ public IBinder onBind(Intent intent) { + mServiceComponent = new ComponentName(this, getClass()); return mSyncAdapter.asBinder(); } /** {@hide} */ - private class AnonymousSyncAdapterImpl extends IAnonymousSyncAdapter.Stub { - + private class SyncAdapterImpl extends ISyncServiceAdapter.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.containsKey(extras)) { + 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 { // 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) { @@ -91,14 +104,15 @@ public abstract class SyncService extends Service { } /** - * Used by the SM to cancel a specific sync using the {@link - * com.android.server.content.SyncManager.ActiveSyncContext} as a handle. + * Used by the SM to cancel a specific sync using the + * com.android.server.content.SyncManager.ActiveSyncContext as a handle. */ @Override public void cancelSync(ISyncContext syncContext) { - AnonymousSyncThread runningSync = null; + SyncThread runningSync = null; synchronized (mSyncThreadLock) { - for (AnonymousSyncThread thread : mSyncThreads.values()) { + for (int i = 0; i < mSyncThreads.size(); i++) { + SyncThread thread = mSyncThreads.valueAt(i); if (thread.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) { runningSync = thread; break; @@ -112,19 +126,39 @@ 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 therefore considers a sync unique - * if the provided bundle is different. + * syncing, this anonymous sync has no notion of account and considers a sync unique if the + * provided bundle is different. */ - private class AnonymousSyncThread extends Thread { + private class SyncThread extends Thread { private final SyncContext mSyncContext; private final Bundle mExtras; + private final int mThreadsKey; - public AnonymousSyncThread(SyncContext syncContext, Bundle extras) { + public SyncThread(SyncContext syncContext, Bundle extras) { mSyncContext = syncContext; mExtras = extras; + mThreadsKey = extrasToKey(extras); } @Override @@ -135,10 +169,8 @@ public abstract class SyncService extends Service { SyncResult syncResult = new SyncResult(); try { - if (isCancelled()) { - return; - } - // Run the sync based off of the provided code. + if (isCancelled()) return; + // Run the sync. SyncService.this.onPerformSync(mExtras, syncResult); } finally { Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER); @@ -146,10 +178,9 @@ 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(mExtras); + mSyncThreads.remove(mThreadsKey); } } } @@ -166,4 +197,14 @@ 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. + * <p>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; + } } |