diff options
Diffstat (limited to 'core/java/android/content/SyncService.java')
-rw-r--r-- | core/java/android/content/SyncService.java | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/core/java/android/content/SyncService.java b/core/java/android/content/SyncService.java new file mode 100644 index 0000000..100fd40 --- /dev/null +++ b/core/java/android/content/SyncService.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content; + +import android.app.Service; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Process; +import android.os.Trace; + +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. + * + * <pre> + * <service ndroid:name=".MyAnonymousSyncService" android:permission="android.permission.SYNC" /> + * </pre> + * Like @link android.content.AbstractThreadedSyncAdapter this service supports + * multiple syncs at the same time. Each incoming startSync() with a unique tag + * will spawn a thread to do the work of that sync. If startSync() is called + * with a tag that already exists, a SyncResult.ALREADY_IN_PROGRESS is returned. + * Remember that your service will spawn multiple threads if you schedule multiple syncs + * at once, so if you mutate local objects you must ensure synchronization. + */ +public abstract class SyncService extends Service { + + /** SyncAdapter Instantiation that any anonymous syncs call. */ + private final AnonymousSyncAdapterImpl mSyncAdapter = new AnonymousSyncAdapterImpl(); + + /** Keep track of on-going syncs, keyed by tag. */ + @GuardedBy("mLock") + private final HashMap<Bundle, AnonymousSyncThread> + mSyncThreads = new HashMap<Bundle, AnonymousSyncThread>(); + /** Lock object for accessing the SyncThreads HashMap. */ + private final Object mSyncThreadLock = new Object(); + + @Override + public IBinder onBind(Intent intent) { + return mSyncAdapter.asBinder(); + } + + /** {@hide} */ + 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; + synchronized (mSyncThreadLock) { + 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) { + syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS); + } + } + + /** + * 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) { + AnonymousSyncThread runningSync = null; + synchronized (mSyncThreadLock) { + for (AnonymousSyncThread thread : mSyncThreads.values()) { + if (thread.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) { + runningSync = thread; + break; + } + } + } + if (runningSync != null) { + runningSync.interrupt(); + } + } + } + + /** + * {@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. + */ + private class AnonymousSyncThread extends Thread { + private final SyncContext mSyncContext; + private final Bundle mExtras; + + public AnonymousSyncThread(SyncContext syncContext, Bundle extras) { + mSyncContext = syncContext; + mExtras = extras; + } + + @Override + public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + + Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, getApplication().getPackageName()); + + SyncResult syncResult = new SyncResult(); + try { + 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); + if (!isCancelled()) { + mSyncContext.onFinished(syncResult); + } + // Synchronize so that the assignment will be seen by other + // threads + // that also synchronize accesses to mSyncThreads. + synchronized (mSyncThreadLock) { + mSyncThreads.remove(mExtras); + } + } + } + + private boolean isCancelled() { + return Thread.currentThread().isInterrupted(); + } + } + + /** + * Initiate an anonymous sync using this service. SyncAdapter-specific + * parameters may be specified in extras, which is guaranteed to not be + * null. + */ + public abstract void onPerformSync(Bundle extras, SyncResult syncResult); + +} |