beef up the syncadapter API
This commit is contained in:
176
core/java/android/content/AbstractThreadedSyncAdapter.java
Normal file
176
core/java/android/content/AbstractThreadedSyncAdapter.java
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2009 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.accounts.Account;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation.
|
||||
* If a sync operation is already in progress when a startSync() request is received then an error
|
||||
* will be returned to the new request and the existing request will be allowed to continue.
|
||||
* When a startSync() is received and there is no sync operation in progress then a thread
|
||||
* will be started to run the operation and {@link #performSync} will be invoked on that thread.
|
||||
* If a cancelSync() is received that matches an existing sync operation then the thread
|
||||
* that is running that sync operation will be interrupted, which will indicate to the thread
|
||||
* that the sync has been canceled.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public abstract class AbstractThreadedSyncAdapter {
|
||||
private final Context mContext;
|
||||
private final AtomicInteger mNumSyncStarts;
|
||||
private final ISyncAdapterImpl mISyncAdapterImpl;
|
||||
|
||||
// all accesses to this member variable must be synchronized on "this"
|
||||
private SyncThread mSyncThread;
|
||||
|
||||
/** Kernel event log tag. Also listed in data/etc/event-log-tags. */
|
||||
public static final int LOG_SYNC_DETAILS = 2743;
|
||||
|
||||
/**
|
||||
* Creates an {@link AbstractThreadedSyncAdapter}.
|
||||
* @param context the {@link Context} that this is running within.
|
||||
*/
|
||||
public AbstractThreadedSyncAdapter(Context context) {
|
||||
mContext = context;
|
||||
mISyncAdapterImpl = new ISyncAdapterImpl();
|
||||
mNumSyncStarts = new AtomicInteger(0);
|
||||
mSyncThread = null;
|
||||
}
|
||||
|
||||
class ISyncAdapterImpl extends ISyncAdapter.Stub {
|
||||
public void startSync(ISyncContext syncContext, String authority, Account account,
|
||||
Bundle extras) {
|
||||
final SyncContext syncContextClient = new SyncContext(syncContext);
|
||||
|
||||
boolean alreadyInProgress;
|
||||
// synchronize to make sure that mSyncThread doesn't change between when we
|
||||
// check it and when we use it
|
||||
synchronized (this) {
|
||||
if (mSyncThread == null) {
|
||||
mSyncThread = new SyncThread(
|
||||
"SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
|
||||
syncContextClient, authority, account, extras);
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
mSyncThread.start();
|
||||
alreadyInProgress = false;
|
||||
} else {
|
||||
alreadyInProgress = true;
|
||||
}
|
||||
}
|
||||
|
||||
// do this outside since we don't want to call back into the syncContext while
|
||||
// holding the synchronization lock
|
||||
if (alreadyInProgress) {
|
||||
syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelSync(ISyncContext syncContext) {
|
||||
// synchronize to make sure that mSyncThread doesn't change between when we
|
||||
// check it and when we use it
|
||||
synchronized (this) {
|
||||
if (mSyncThread != null
|
||||
&& mSyncThread.mSyncContext.getISyncContext() == syncContext) {
|
||||
mSyncThread.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The thread that invokes performSync(). It also acquires the provider for this sync
|
||||
* before calling performSync and releases it afterwards. Cancel this thread in order to
|
||||
* cancel the sync.
|
||||
*/
|
||||
private class SyncThread extends Thread {
|
||||
private final SyncContext mSyncContext;
|
||||
private final String mAuthority;
|
||||
private final Account mAccount;
|
||||
private final Bundle mExtras;
|
||||
|
||||
private SyncThread(String name, SyncContext syncContext, String authority,
|
||||
Account account, Bundle extras) {
|
||||
super(name);
|
||||
mSyncContext = syncContext;
|
||||
mAuthority = authority;
|
||||
mAccount = account;
|
||||
mExtras = extras;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SyncResult syncResult = new SyncResult();
|
||||
ContentProviderClient provider = null;
|
||||
try {
|
||||
provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
|
||||
if (provider != null) {
|
||||
AbstractThreadedSyncAdapter.this.performSync(mAccount, mExtras,
|
||||
mAuthority, provider, syncResult);
|
||||
} else {
|
||||
// TODO(fredq) update the syncResults to indicate that we were unable to
|
||||
// find the provider. maybe with a ProviderError?
|
||||
}
|
||||
} finally {
|
||||
if (provider != null) {
|
||||
provider.release();
|
||||
}
|
||||
if (!isCanceled()) {
|
||||
mSyncContext.onFinished(syncResult);
|
||||
}
|
||||
// synchronize so that the assignment will be seen by other threads
|
||||
// that also synchronize accesses to mSyncThread
|
||||
synchronized (this) {
|
||||
mSyncThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCanceled() {
|
||||
return Thread.currentThread().isInterrupted();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a reference to the ISyncAdapter interface into this SyncAdapter implementation.
|
||||
*/
|
||||
public final ISyncAdapter getISyncAdapter() {
|
||||
return mISyncAdapterImpl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a sync for this account. SyncAdapter-specific parameters may
|
||||
* be specified in extras, which is guaranteed to not be null. Invocations
|
||||
* of this method are guaranteed to be serialized.
|
||||
*
|
||||
* @param account the account that should be synced
|
||||
* @param extras SyncAdapter-specific parameters
|
||||
* @param authority the authority of this sync request
|
||||
* @param provider a ContentProviderClient that points to the ContentProvider for this
|
||||
* authority
|
||||
* @param syncResult SyncAdapter-specific parameters
|
||||
*/
|
||||
public abstract void performSync(Account account, Bundle extras,
|
||||
String authority, ContentProviderClient provider, SyncResult syncResult);
|
||||
}
|
||||
@@ -31,14 +31,17 @@ oneway interface ISyncAdapter {
|
||||
*
|
||||
* @param syncContext the ISyncContext used to indicate the progress of the sync. When
|
||||
* the sync is finished (successfully or not) ISyncContext.onFinished() must be called.
|
||||
* @param authority the authority that should be synced
|
||||
* @param account the account that should be synced
|
||||
* @param extras SyncAdapter-specific parameters
|
||||
*/
|
||||
void startSync(ISyncContext syncContext, in Account account, in Bundle extras);
|
||||
void startSync(ISyncContext syncContext, String authority,
|
||||
in Account account, in Bundle extras);
|
||||
|
||||
/**
|
||||
* Cancel the most recently initiated sync. Due to race conditions, this may arrive
|
||||
* after the ISyncContext.onFinished() for that sync was called.
|
||||
* @param syncContext the ISyncContext that was passed to {@link #startSync}
|
||||
*/
|
||||
void cancelSync();
|
||||
void cancelSync(ISyncContext syncContext);
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ public abstract class SyncAdapter {
|
||||
public static final int LOG_SYNC_DETAILS = 2743;
|
||||
|
||||
class Transport extends ISyncAdapter.Stub {
|
||||
public void startSync(ISyncContext syncContext, Account account,
|
||||
public void startSync(ISyncContext syncContext, String authority, Account account,
|
||||
Bundle extras) throws RemoteException {
|
||||
SyncAdapter.this.startSync(new SyncContext(syncContext), account, extras);
|
||||
}
|
||||
|
||||
public void cancelSync() throws RemoteException {
|
||||
public void cancelSync(ISyncContext syncContext) throws RemoteException {
|
||||
SyncAdapter.this.cancelSync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 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.os.*;
|
||||
import android.os.Process;
|
||||
import android.accounts.Account;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public abstract class SyncAdapterNew {
|
||||
private static final String TAG = "SyncAdapter";
|
||||
private final Context mContext;
|
||||
private final String mAuthority;
|
||||
|
||||
/** Kernel event log tag. Also listed in data/etc/event-log-tags. */
|
||||
public static final int LOG_SYNC_DETAILS = 2743;
|
||||
|
||||
public SyncAdapterNew(Context context, String authority) {
|
||||
mContext = context;
|
||||
mAuthority = authority;
|
||||
}
|
||||
|
||||
class Transport extends ISyncAdapter.Stub {
|
||||
private final AtomicInteger mNumSyncStarts = new AtomicInteger(0);
|
||||
private volatile Thread mSyncThread;
|
||||
|
||||
public void startSync(ISyncContext syncContext, Account account, Bundle extras) {
|
||||
boolean alreadyInProgress;
|
||||
synchronized (this) {
|
||||
if (mSyncThread == null) {
|
||||
mSyncThread = new Thread(
|
||||
new SyncRunnable(new SyncContext(syncContext), account, extras),
|
||||
"SyncAdapterThread-" + mNumSyncStarts.incrementAndGet());
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
mSyncThread.start();
|
||||
alreadyInProgress = false;
|
||||
} else {
|
||||
alreadyInProgress = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (alreadyInProgress) {
|
||||
try {
|
||||
syncContext.onFinished(SyncResult.ALREADY_IN_PROGRESS);
|
||||
} catch (RemoteException e) {
|
||||
// don't care if the caller is no longer around
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelSync() {
|
||||
synchronized (this) {
|
||||
if (mSyncThread != null) {
|
||||
mSyncThread.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SyncRunnable implements Runnable {
|
||||
private final SyncContext mSyncContext;
|
||||
private final Account mAccount;
|
||||
private final Bundle mExtras;
|
||||
|
||||
private SyncRunnable(SyncContext syncContext, Account account, Bundle extras) {
|
||||
mSyncContext = syncContext;
|
||||
mAccount = account;
|
||||
mExtras = extras;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (isCanceled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SyncResult syncResult = new SyncResult();
|
||||
ContentProviderClient provider = mAuthority != null
|
||||
? mContext.getContentResolver().acquireContentProviderClient(mAuthority)
|
||||
: null;
|
||||
try {
|
||||
SyncAdapterNew.this.performSync(mAccount, mExtras, provider, syncResult);
|
||||
} finally {
|
||||
if (provider != null) {
|
||||
provider.release();
|
||||
}
|
||||
if (!isCanceled()) {
|
||||
mSyncContext.onFinished(syncResult);
|
||||
}
|
||||
mSyncThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCanceled() {
|
||||
return Thread.currentThread().isInterrupted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Transport mTransport = new Transport();
|
||||
|
||||
/**
|
||||
* Get the Transport object.
|
||||
*/
|
||||
public final ISyncAdapter getISyncAdapter() {
|
||||
return mTransport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a sync for this account. SyncAdapter-specific parameters may
|
||||
* be specified in extras, which is guaranteed to not be null. Invocations
|
||||
* of this method are guaranteed to be serialized.
|
||||
*
|
||||
* @param account the account that should be synced
|
||||
* @param extras SyncAdapter-specific parameters
|
||||
*/
|
||||
public abstract void performSync(Account account, Bundle extras,
|
||||
ContentProviderClient provider, SyncResult syncResult);
|
||||
}
|
||||
@@ -33,8 +33,6 @@ import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.RegisteredServicesCache;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
@@ -50,10 +48,7 @@ import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.SystemProperties;
|
||||
import android.provider.Sync;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Sync.History;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.format.Time;
|
||||
import android.util.Config;
|
||||
@@ -77,8 +72,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Random;
|
||||
import java.util.Observer;
|
||||
import java.util.Observable;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -1435,7 +1428,7 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
// outstanding
|
||||
if (mActiveSyncContext.mSyncAdapter != null) {
|
||||
try {
|
||||
mActiveSyncContext.mSyncAdapter.cancelSync();
|
||||
mActiveSyncContext.mSyncAdapter.cancelSync(mActiveSyncContext);
|
||||
} catch (RemoteException e) {
|
||||
// we don't need to retry this in this case
|
||||
}
|
||||
@@ -1678,8 +1671,8 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
mActiveSyncContext.mSyncAdapter = syncAdapter;
|
||||
final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
|
||||
try {
|
||||
syncAdapter.startSync(mActiveSyncContext, syncOperation.account,
|
||||
syncOperation.extras);
|
||||
syncAdapter.startSync(mActiveSyncContext, syncOperation.authority,
|
||||
syncOperation.account, syncOperation.extras);
|
||||
} catch (RemoteException remoteExc) {
|
||||
if (Config.LOGD) {
|
||||
Log.d(TAG, "runStateIdle: caught a RemoteException, rescheduling", remoteExc);
|
||||
@@ -1742,7 +1735,7 @@ class SyncManager implements OnAccountsUpdatedListener {
|
||||
}
|
||||
if (activeSyncContext.mSyncAdapter != null) {
|
||||
try {
|
||||
activeSyncContext.mSyncAdapter.cancelSync();
|
||||
activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
|
||||
} catch (RemoteException e) {
|
||||
// we don't need to retry this in this case
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user