summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorMatthew Williams <mjwilliams@google.com>2013-07-26 12:56:39 -0700
committerMatthew Williams <mjwilliams@google.com>2013-09-05 15:58:24 -0700
commit56dbf8f23677d28615e61ef2fbb0e738cca02528 (patch)
tree0b69d2fd85af218ef72ef39df30967519749aa1c /core
parentc213bdea26019acccfc835b9ab8d63ba2130c7f9 (diff)
downloadframeworks_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.java160
-rw-r--r--core/java/android/content/IContentService.aidl64
-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.java7
-rw-r--r--core/java/android/content/SyncInfo.java23
-rw-r--r--core/java/android/content/SyncRequest.java103
-rw-r--r--core/java/android/content/SyncService.java117
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>
- * &lt;service ndroid:name=".MyAnonymousSyncService" android:permission="android.permission.SYNC" /&gt;
+ * &lt;service android:name=".MySyncService"/&gt;
* </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;
+ }
}