summaryrefslogtreecommitdiffstats
path: root/core/java/android/content/SyncService.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/content/SyncService.java')
-rw-r--r--core/java/android/content/SyncService.java117
1 files changed, 79 insertions, 38 deletions
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;
+ }
}