summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFred Quintana <fredq@google.com>2010-11-03 17:02:55 -0700
committerFred Quintana <fredq@google.com>2010-12-09 14:24:34 -0800
commit0c4d04ac2e8aa62560d8d767fa1c87e5361b0b08 (patch)
tree35abdc4bd85808b46663105491fa0d64bb07f0de
parent6a843d03c7eaf4fa3c11798585626d68d81a9304 (diff)
downloadframeworks_base-0c4d04ac2e8aa62560d8d767fa1c87e5361b0b08.zip
frameworks_base-0c4d04ac2e8aa62560d8d767fa1c87e5361b0b08.tar.gz
frameworks_base-0c4d04ac2e8aa62560d8d767fa1c87e5361b0b08.tar.bz2
allow sync adapter authors to control more policies
- let the SyncManager know that the SyncAdapter can handle parallel syncs even within sync adapter types - allow indicating that the sync adapter should be auto initialized without requiring the sync adapter to run first. When this setting is used then setIsSyncable(1) is automatically called for the sync adapter. Change-Id: Ib40eba95c2556eaee4bb0fe715f379af1b72b84a
-rw-r--r--api/current.xml117
-rw-r--r--core/java/android/content/AbstractThreadedSyncAdapter.java86
-rw-r--r--core/java/android/content/SyncAdapterType.java66
-rw-r--r--core/java/android/content/SyncAdaptersCache.java13
-rw-r--r--core/java/android/content/SyncManager.java158
-rw-r--r--core/java/android/content/SyncOperation.java7
-rw-r--r--core/java/android/content/SyncQueue.java12
-rwxr-xr-xcore/res/res/values/attrs.xml19
-rw-r--r--core/res/res/values/public.xml5
-rw-r--r--core/tests/coretests/src/android/content/SyncOperationTest.java15
10 files changed, 355 insertions, 143 deletions
diff --git a/api/current.xml b/api/current.xml
index a33d30f..acc7e3d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -2143,6 +2143,17 @@
visibility="public"
>
</field>
+<field name="allowParallelSyncs"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843585"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="allowSingleTap"
type="int"
transient="false"
@@ -5388,6 +5399,17 @@
visibility="public"
>
</field>
+<field name="isAlwaysSyncable"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843586"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="isDefault"
type="int"
transient="false"
@@ -42266,6 +42288,20 @@
<parameter name="autoInitialize" type="boolean">
</parameter>
</constructor>
+<constructor name="AbstractThreadedSyncAdapter"
+ type="android.content.AbstractThreadedSyncAdapter"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="autoInitialize" type="boolean">
+</parameter>
+<parameter name="allowParallelSyncs" type="boolean">
+</parameter>
+</constructor>
<method name="getContext"
return="android.content.Context"
abstract="false"
@@ -42320,6 +42356,19 @@
visibility="public"
>
</method>
+<method name="onSyncCanceled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="thread" type="java.lang.Thread">
+</parameter>
+</method>
<field name="LOG_SYNC_DETAILS"
type="int"
transient="false"
@@ -55429,25 +55478,20 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="authority" type="java.lang.String">
-</parameter>
-<parameter name="accountType" type="java.lang.String">
-</parameter>
-<parameter name="userVisible" type="boolean">
-</parameter>
-<parameter name="supportsUploading" type="boolean">
+<parameter name="source" type="android.os.Parcel">
</parameter>
</constructor>
-<constructor name="SyncAdapterType"
- type="android.content.SyncAdapterType"
+<method name="allowParallelSyncs"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
visibility="public"
>
-<parameter name="source" type="android.os.Parcel">
-</parameter>
-</constructor>
+</method>
<method name="describeContents"
return="int"
abstract="false"
@@ -55459,6 +55503,17 @@
visibility="public"
>
</method>
+<method name="isAlwaysSyncable"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isUserVisible"
return="boolean"
abstract="false"
@@ -250525,7 +250580,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
@@ -264775,9 +264830,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="start" type="int">
+<parameter name="beginIndex" type="int">
</parameter>
-<parameter name="end" type="int">
+<parameter name="endIndex" type="int">
</parameter>
</method>
<method name="ensureCapacity"
@@ -267475,7 +267530,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -273798,7 +273853,7 @@
</exception>
</method>
<method name="getClass"
- return="java.lang.Class&lt;?&gt;"
+ return="java.lang.Class&lt;? extends java.lang.Object&gt;"
abstract="false"
native="true"
synchronized="false"
@@ -276588,9 +276643,9 @@
>
<parameter name="data" type="byte[]">
</parameter>
-<parameter name="offset" type="int">
+<parameter name="start" type="int">
</parameter>
-<parameter name="byteCount" type="int">
+<parameter name="length" type="int">
</parameter>
</constructor>
<constructor name="String"
@@ -276604,9 +276659,9 @@
</parameter>
<parameter name="high" type="int">
</parameter>
-<parameter name="offset" type="int">
+<parameter name="start" type="int">
</parameter>
-<parameter name="byteCount" type="int">
+<parameter name="length" type="int">
</parameter>
</constructor>
<constructor name="String"
@@ -276618,9 +276673,9 @@
>
<parameter name="data" type="byte[]">
</parameter>
-<parameter name="offset" type="int">
+<parameter name="start" type="int">
</parameter>
-<parameter name="byteCount" type="int">
+<parameter name="length" type="int">
</parameter>
<parameter name="charsetName" type="java.lang.String">
</parameter>
@@ -276650,9 +276705,9 @@
>
<parameter name="data" type="byte[]">
</parameter>
-<parameter name="offset" type="int">
+<parameter name="start" type="int">
</parameter>
-<parameter name="byteCount" type="int">
+<parameter name="length" type="int">
</parameter>
<parameter name="charset" type="java.nio.charset.Charset">
</parameter>
@@ -276688,9 +276743,9 @@
>
<parameter name="data" type="char[]">
</parameter>
-<parameter name="offset" type="int">
+<parameter name="start" type="int">
</parameter>
-<parameter name="charCount" type="int">
+<parameter name="length" type="int">
</parameter>
</constructor>
<constructor name="String"
@@ -276710,7 +276765,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="stringBuffer" type="java.lang.StringBuffer">
+<parameter name="stringbuffer" type="java.lang.StringBuffer">
</parameter>
</constructor>
<constructor name="String"
@@ -276734,7 +276789,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="stringBuilder" type="java.lang.StringBuilder">
+<parameter name="sb" type="java.lang.StringBuilder">
</parameter>
</constructor>
<method name="charAt"
@@ -276786,9 +276841,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="start" type="int">
+<parameter name="beginIndex" type="int">
</parameter>
-<parameter name="end" type="int">
+<parameter name="endIndex" type="int">
</parameter>
</method>
<method name="compareTo"
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index c0a268f..90d6472 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -22,6 +22,7 @@ import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
+import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -47,10 +48,11 @@ public abstract class AbstractThreadedSyncAdapter {
private final ISyncAdapterImpl mISyncAdapterImpl;
// all accesses to this member variable must be synchronized on mSyncThreadLock
- private SyncThread mSyncThread;
+ private final HashMap<Account, SyncThread> mSyncThreads = new HashMap<Account, SyncThread>();
private final Object mSyncThreadLock = new Object();
private final boolean mAutoInitialize;
+ private boolean mAllowParallelSyncs;
/**
* Creates an {@link AbstractThreadedSyncAdapter}.
@@ -62,27 +64,53 @@ public abstract class AbstractThreadedSyncAdapter {
* is currently set to <0.
*/
public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) {
+ this(context, autoInitialize, false /* allowParallelSyncs */);
+ }
+
+ /**
+ * Creates an {@link AbstractThreadedSyncAdapter}.
+ * @param context the {@link android.content.Context} that this is running within.
+ * @param autoInitialize if true then sync requests that have
+ * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
+ * {@link AbstractThreadedSyncAdapter} by calling
+ * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
+ * is currently set to <0.
+ * @param allowParallelSyncs if true then allow syncs for different accounts to run
+ * at the same time, each in their own thread. This must be consistent with the setting
+ * in the SyncAdapter's configuration file.
+ */
+ public AbstractThreadedSyncAdapter(Context context,
+ boolean autoInitialize, boolean allowParallelSyncs) {
mContext = context;
mISyncAdapterImpl = new ISyncAdapterImpl();
mNumSyncStarts = new AtomicInteger(0);
- mSyncThread = null;
mAutoInitialize = autoInitialize;
+ mAllowParallelSyncs = allowParallelSyncs;
}
public Context getContext() {
return mContext;
}
+ private Account toSyncKey(Account account) {
+ if (mAllowParallelSyncs) {
+ return account;
+ } else {
+ return null;
+ }
+ }
+
private 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
+ // synchronize to make sure that mSyncThreads doesn't change between when we
// check it and when we use it
+ final Account threadsKey = toSyncKey(account);
synchronized (mSyncThreadLock) {
- if (mSyncThread == null) {
+ if (!mSyncThreads.containsKey(threadsKey)) {
if (mAutoInitialize
&& extras != null
&& extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
@@ -92,10 +120,11 @@ public abstract class AbstractThreadedSyncAdapter {
syncContextClient.onFinished(new SyncResult());
return;
}
- mSyncThread = new SyncThread(
+ SyncThread syncThread = new SyncThread(
"SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
syncContextClient, authority, account, extras);
- mSyncThread.start();
+ mSyncThreads.put(threadsKey, syncThread);
+ syncThread.start();
alreadyInProgress = false;
} else {
alreadyInProgress = true;
@@ -110,15 +139,23 @@ public abstract class AbstractThreadedSyncAdapter {
}
public void cancelSync(ISyncContext syncContext) {
- // synchronize to make sure that mSyncThread doesn't change between when we
+ // synchronize to make sure that mSyncThreads doesn't change between when we
// check it and when we use it
- final SyncThread syncThread;
+ SyncThread info = null;
synchronized (mSyncThreadLock) {
- syncThread = mSyncThread;
+ for (SyncThread current : mSyncThreads.values()) {
+ if (current.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
+ info = current;
+ break;
+ }
+ }
}
- if (syncThread != null
- && syncThread.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
- onSyncCanceled();
+ if (info != null) {
+ if (mAllowParallelSyncs) {
+ onSyncCanceled(info);
+ } else {
+ onSyncCanceled();
+ }
}
}
@@ -139,6 +176,7 @@ public abstract class AbstractThreadedSyncAdapter {
private final String mAuthority;
private final Account mAccount;
private final Bundle mExtras;
+ private final Account mThreadsKey;
private SyncThread(String name, SyncContext syncContext, String authority,
Account account, Bundle extras) {
@@ -147,6 +185,7 @@ public abstract class AbstractThreadedSyncAdapter {
mAuthority = authority;
mAccount = account;
mExtras = extras;
+ mThreadsKey = toSyncKey(account);
}
public void run() {
@@ -174,9 +213,9 @@ public abstract class AbstractThreadedSyncAdapter {
mSyncContext.onFinished(syncResult);
}
// synchronize so that the assignment will be seen by other threads
- // that also synchronize accesses to mSyncThread
+ // that also synchronize accesses to mSyncThreads
synchronized (mSyncThreadLock) {
- mSyncThread = null;
+ mSyncThreads.remove(mThreadsKey);
}
}
}
@@ -212,15 +251,30 @@ public abstract class AbstractThreadedSyncAdapter {
* Indicates that a sync operation has been canceled. This will be invoked on a separate
* thread than the sync thread and so you must consider the multi-threaded implications
* of the work that you do in this method.
- *
+ * <p>
+ * This will only be invoked when the SyncAdapter indicates that it doesn't support
+ * parallel syncs.
*/
public void onSyncCanceled() {
final SyncThread syncThread;
synchronized (mSyncThreadLock) {
- syncThread = mSyncThread;
+ syncThread = mSyncThreads.get(null);
}
if (syncThread != null) {
syncThread.interrupt();
}
}
+
+ /**
+ * Indicates that a sync operation has been canceled. This will be invoked on a separate
+ * thread than the sync thread and so you must consider the multi-threaded implications
+ * of the work that you do in this method.
+ * <p>
+ * This will only be invoked when the SyncAdapter indicates that it does support
+ * parallel syncs.
+ * @param thread the Thread of the sync that is to be canceled.
+ */
+ public void onSyncCanceled(Thread thread) {
+ thread.interrupt();
+ }
}
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
index 25cbdb1..787c757 100644
--- a/core/java/android/content/SyncAdapterType.java
+++ b/core/java/android/content/SyncAdapterType.java
@@ -30,8 +30,11 @@ public class SyncAdapterType implements Parcelable {
public final boolean isKey;
private final boolean userVisible;
private final boolean supportsUploading;
+ private final boolean isAlwaysSyncable;
+ private final boolean allowParallelSyncs;
- public SyncAdapterType(String authority, String accountType, boolean userVisible,
+ /** @hide */
+ public SyncAdapterType(String authority, String accountType, boolean userVisible,
boolean supportsUploading) {
if (TextUtils.isEmpty(authority)) {
throw new IllegalArgumentException("the authority must not be empty: " + authority);
@@ -43,6 +46,28 @@ public class SyncAdapterType implements Parcelable {
this.accountType = accountType;
this.userVisible = userVisible;
this.supportsUploading = supportsUploading;
+ this.isAlwaysSyncable = false;
+ this.allowParallelSyncs = false;
+ this.isKey = false;
+ }
+
+ /** @hide */
+ public SyncAdapterType(String authority, String accountType, boolean userVisible,
+ boolean supportsUploading,
+ boolean isAlwaysSyncable,
+ boolean allowParallelSyncs) {
+ if (TextUtils.isEmpty(authority)) {
+ throw new IllegalArgumentException("the authority must not be empty: " + authority);
+ }
+ if (TextUtils.isEmpty(accountType)) {
+ throw new IllegalArgumentException("the accountType must not be empty: " + accountType);
+ }
+ this.authority = authority;
+ this.accountType = accountType;
+ this.userVisible = userVisible;
+ this.supportsUploading = supportsUploading;
+ this.isAlwaysSyncable = isAlwaysSyncable;
+ this.allowParallelSyncs = allowParallelSyncs;
this.isKey = false;
}
@@ -57,6 +82,8 @@ public class SyncAdapterType implements Parcelable {
this.accountType = accountType;
this.userVisible = true;
this.supportsUploading = true;
+ this.isAlwaysSyncable = false;
+ this.allowParallelSyncs = false;
this.isKey = true;
}
@@ -76,6 +103,35 @@ public class SyncAdapterType implements Parcelable {
return userVisible;
}
+ /**
+ * @return True if this SyncAdapter supports syncing multiple accounts simultaneously.
+ * If false then the SyncManager will take care to only start one sync at a time
+ * using this SyncAdapter.
+ */
+ public boolean allowParallelSyncs() {
+ if (isKey) {
+ throw new IllegalStateException(
+ "this method is not allowed to be called when this is a key");
+ }
+ return allowParallelSyncs;
+ }
+
+ /**
+ * If true then the SyncManager will never issue an initialization sync to the SyncAdapter
+ * and will instead automatically call
+ * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with a
+ * value of 1 for each account and provider that this sync adapter supports.
+ * @return true if the SyncAdapter does not require initialization and if it is ok for the
+ * SyncAdapter to treat it as syncable automatically.
+ */
+ public boolean isAlwaysSyncable() {
+ if (isKey) {
+ throw new IllegalStateException(
+ "this method is not allowed to be called when this is a key");
+ }
+ return isAlwaysSyncable;
+ }
+
public static SyncAdapterType newKey(String authority, String accountType) {
return new SyncAdapterType(authority, accountType);
}
@@ -106,6 +162,8 @@ public class SyncAdapterType implements Parcelable {
+ ", type=" + accountType
+ ", userVisible=" + userVisible
+ ", supportsUploading=" + supportsUploading
+ + ", isAlwaysSyncable=" + isAlwaysSyncable
+ + ", allowParallelSyncs=" + allowParallelSyncs
+ "}";
}
}
@@ -123,6 +181,8 @@ public class SyncAdapterType implements Parcelable {
dest.writeString(accountType);
dest.writeInt(userVisible ? 1 : 0);
dest.writeInt(supportsUploading ? 1 : 0);
+ dest.writeInt(isAlwaysSyncable ? 1 : 0);
+ dest.writeInt(allowParallelSyncs ? 1 : 0);
}
public SyncAdapterType(Parcel source) {
@@ -130,6 +190,8 @@ public class SyncAdapterType implements Parcelable {
source.readString(),
source.readString(),
source.readInt() != 0,
+ source.readInt() != 0,
+ source.readInt() != 0,
source.readInt() != 0);
}
@@ -142,4 +204,4 @@ public class SyncAdapterType implements Parcelable {
return new SyncAdapterType[size];
}
};
-} \ No newline at end of file
+}
diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java
index 98a2595..33a713b 100644
--- a/core/java/android/content/SyncAdaptersCache.java
+++ b/core/java/android/content/SyncAdaptersCache.java
@@ -60,7 +60,14 @@ import java.io.IOException;
final boolean supportsUploading =
sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_supportsUploading,
true);
- return new SyncAdapterType(authority, accountType, userVisible, supportsUploading);
+ final boolean isAlwaysSyncable =
+ sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_isAlwaysSyncable,
+ false);
+ final boolean allowParallelSyncs =
+ sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_allowParallelSyncs,
+ false);
+ return new SyncAdapterType(authority, accountType, userVisible, supportsUploading,
+ isAlwaysSyncable, allowParallelSyncs);
} finally {
sa.recycle();
}
@@ -71,7 +78,7 @@ import java.io.IOException;
out.attribute(null, "authority", item.authority);
out.attribute(null, "accountType", item.accountType);
}
-
+
public SyncAdapterType createFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
final String authority = parser.getAttributeValue(null, "authority");
@@ -79,4 +86,4 @@ import java.io.IOException;
return SyncAdapterType.newKey(authority, accountType);
}
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index a6ffed6..dde198e 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -17,6 +17,7 @@
package android.content;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -134,10 +135,10 @@ public class SyncManager implements OnAccountsUpdateListener {
private final NotificationManager mNotificationMgr;
private AlarmManager mAlarmService = null;
- private final SyncStorageEngine mSyncStorageEngine;
- public final SyncQueue mSyncQueue;
+ private SyncStorageEngine mSyncStorageEngine;
+ public SyncQueue mSyncQueue;
- private final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
+ protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
// set if the sync active indicator should be reported
private boolean mNeedSyncActiveNotification = false;
@@ -147,7 +148,7 @@ public class SyncManager implements OnAccountsUpdateListener {
// its accessor, getConnManager().
private ConnectivityManager mConnManagerDoNotUseDirectly;
- private final SyncAdaptersCache mSyncAdapters;
+ protected SyncAdaptersCache mSyncAdapters;
private BroadcastReceiver mStorageIntentReceiver =
new BroadcastReceiver() {
@@ -300,11 +301,11 @@ public class SyncManager implements OnAccountsUpdateListener {
public SyncManager(Context context, boolean factoryTest) {
// Initialize the SyncStorageEngine first, before registering observers
// and creating threads and so on; it may fail if the disk is full.
+ mContext = context;
SyncStorageEngine.init(context);
mSyncStorageEngine = SyncStorageEngine.getSingleton();
- mSyncQueue = new SyncQueue(mSyncStorageEngine);
-
- mContext = context;
+ mSyncAdapters = new SyncAdaptersCache(mContext);
+ mSyncQueue = new SyncQueue(mSyncStorageEngine, mSyncAdapters);
HandlerThread syncThread = new HandlerThread("SyncHandlerThread",
Process.THREAD_PRIORITY_BACKGROUND);
@@ -312,7 +313,6 @@ public class SyncManager implements OnAccountsUpdateListener {
mSyncHandler = new SyncHandler(syncThread.getLooper());
mMainHandler = new Handler(mContext.getMainLooper());
- mSyncAdapters = new SyncAdaptersCache(mContext);
mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
public void onServiceChanged(SyncAdapterType type, boolean removed) {
if (!removed) {
@@ -588,54 +588,71 @@ public class SyncManager implements OnAccountsUpdateListener {
if (isSyncable == 0) {
continue;
}
- if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
- continue;
- }
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
mSyncAdapters.getServiceInfo(
SyncAdapterType.newKey(authority, account.type));
- if (syncAdapterInfo != null) {
- if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
- continue;
- }
+ if (syncAdapterInfo == null) {
+ continue;
+ }
+ final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
+ final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
+ if (isSyncable < 0 && isAlwaysSyncable) {
+ mSyncStorageEngine.setIsSyncable(account, authority, 1);
+ isSyncable = 1;
+ }
+ if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
+ continue;
+ }
+ if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
+ continue;
+ }
- // always allow if the isSyncable state is unknown
- boolean syncAllowed =
- (isSyncable < 0)
- || ignoreSettings
- || (backgroundDataUsageAllowed && masterSyncAutomatically
- && mSyncStorageEngine.getSyncAutomatically(account, authority));
- if (!syncAllowed) {
- if (isLoggable) {
- Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
- + " is not allowed, dropping request");
- }
- continue;
+ // always allow if the isSyncable state is unknown
+ boolean syncAllowed =
+ (isSyncable < 0)
+ || ignoreSettings
+ || (backgroundDataUsageAllowed && masterSyncAutomatically
+ && mSyncStorageEngine.getSyncAutomatically(account, authority));
+ if (!syncAllowed) {
+ if (isLoggable) {
+ Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
+ + " is not allowed, dropping request");
}
+ continue;
+ }
- Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(account, authority);
- long delayUntil = mSyncStorageEngine.getDelayUntilTime(account, authority);
- final long backoffTime = backoff != null ? backoff.first : 0;
- if (isSyncable < 0) {
- Bundle newExtras = new Bundle();
- newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
- scheduleSyncOperation(
- new SyncOperation(account, source, authority, newExtras, 0,
- backoffTime, delayUntil));
+ Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(account, authority);
+ long delayUntil = mSyncStorageEngine.getDelayUntilTime(account, authority);
+ final long backoffTime = backoff != null ? backoff.first : 0;
+ if (isSyncable < 0) {
+ Bundle newExtras = new Bundle();
+ newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
+ if (isLoggable) {
+ Log.v(TAG, "scheduleSync:"
+ + " delay " + delay
+ + ", source " + source
+ + ", account " + account
+ + ", authority " + authority
+ + ", extras " + newExtras);
}
- if (!onlyThoseWithUnkownSyncableState) {
- if (isLoggable) {
- Log.v(TAG, "scheduleSync:"
- + " delay " + delay
- + ", source " + source
- + ", account " + account
- + ", authority " + authority
- + ", extras " + extras);
- }
- scheduleSyncOperation(
- new SyncOperation(account, source, authority, extras, delay,
- backoffTime, delayUntil));
+ scheduleSyncOperation(
+ new SyncOperation(account, source, authority, newExtras, 0,
+ backoffTime, delayUntil,
+ allowParallelSyncs));
+ }
+ if (!onlyThoseWithUnkownSyncableState) {
+ if (isLoggable) {
+ Log.v(TAG, "scheduleSync:"
+ + " delay " + delay
+ + ", source " + source
+ + ", account " + account
+ + ", authority " + authority
+ + ", extras " + extras);
}
+ scheduleSyncOperation(
+ new SyncOperation(account, source, authority, extras, delay,
+ backoffTime, delayUntil,
+ allowParallelSyncs));
}
}
}
@@ -857,7 +874,7 @@ public class SyncManager implements OnAccountsUpdateListener {
scheduleSyncOperation(new SyncOperation(operation.account, operation.syncSource,
operation.authority, operation.extras,
DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000,
- operation.backoff, operation.delayUntil));
+ operation.backoff, operation.delayUntil, operation.allowParallelSyncs));
} else if (syncResult.hasSoftError()) {
if (isLoggable) {
Log.d(TAG, "retrying sync operation because it encountered a soft error: "
@@ -1530,12 +1547,19 @@ public class SyncManager implements OnAccountsUpdateListener {
if (nextPollTimeAbsolute <= nowAbsolute) {
final Pair<Long, Long> backoff =
mSyncStorageEngine.getBackoff(info.account, info.authority);
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+ mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(info.authority, info.account.type));
+ if (syncAdapterInfo == null) {
+ continue;
+ }
scheduleSyncOperation(
new SyncOperation(info.account, SyncStorageEngine.SOURCE_PERIODIC,
info.authority, extras, 0 /* delay */,
backoff != null ? backoff.first : 0,
mSyncStorageEngine.getDelayUntilTime(
- info.account, info.authority)));
+ info.account, info.authority),
+ syncAdapterInfo.type.allowParallelSyncs()));
status.setPeriodicSyncTime(i, nowAbsolute);
} else {
// it isn't ready to run, remember this time if it is earlier than
@@ -1678,7 +1702,9 @@ public class SyncManager implements OnAccountsUpdateListener {
numRegular++;
}
if (activeOp.account.type.equals(candidate.account.type)
- && activeOp.authority.equals(candidate.authority)) {
+ && activeOp.authority.equals(candidate.authority)
+ && (!activeOp.allowParallelSyncs
+ || activeOp.account.name.equals(candidate.account.name))) {
conflict = activeSyncContext;
// don't break out since we want to do a full count of the varieties
} else {
@@ -1717,8 +1743,8 @@ public class SyncManager implements OnAccountsUpdateListener {
continue;
}
} else {
- final boolean roomAvailable = candidateIsInitialization
- ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS
+ final boolean roomAvailable = candidateIsInitialization
+ ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS
: numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS;
if (roomAvailable) {
// dispatch candidate
@@ -1739,7 +1765,6 @@ public class SyncManager implements OnAccountsUpdateListener {
runSyncFinishedOrCanceledLocked(null, toReschedule);
scheduleSyncOperation(toReschedule.mSyncOperation);
}
-
synchronized (mSyncQueue){
mSyncQueue.remove(candidate);
}
@@ -1751,33 +1776,13 @@ public class SyncManager implements OnAccountsUpdateListener {
private boolean dispatchSyncOperation(SyncOperation op) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "maybeStartNextSync: we are going to sync " + op);
+ Log.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
for (ActiveSyncContext syncContext : mActiveSyncContexts) {
Log.v(TAG, syncContext.toString());
}
}
- // if this is an initialization sync and there is already a sync running with
- // the same account type and authority cancel that sync before starting this one
- // since otherwise the syncadapter will likely reject this request
- if (op.isInitialization()) {
- Iterator<ActiveSyncContext> iterator = mActiveSyncContexts.iterator();
- while (iterator.hasNext()) {
- ActiveSyncContext syncContext = iterator.next();
- if (!syncContext.mSyncOperation.isInitialization()
- && syncContext.mSyncOperation.account.type.equals(op.account.type)
- && syncContext.mSyncOperation.authority.equals(op.authority)) {
- Log.d(TAG, "canceling and rescheduling " + syncContext.mSyncOperation
- + " since we are about to start a sync that used the "
- + "same sync adapter, " + op);
- iterator.remove();
- runSyncFinishedOrCanceledLocked(null, syncContext);
- scheduleSyncOperation(syncContext.mSyncOperation);
- }
- }
- }
-
// connect to the sync adapter
SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type);
RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
@@ -1923,7 +1928,8 @@ public class SyncManager implements OnAccountsUpdateListener {
if (syncResult != null && syncResult.fullSyncRequested) {
scheduleSyncOperation(new SyncOperation(syncOperation.account,
syncOperation.syncSource, syncOperation.authority, new Bundle(), 0,
- syncOperation.backoff, syncOperation.delayUntil));
+ syncOperation.backoff, syncOperation.delayUntil,
+ syncOperation.allowParallelSyncs));
}
// no need to schedule an alarm, as that will be done by our caller.
}
diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java
index 3d7f3fbf..94c2247 100644
--- a/core/java/android/content/SyncOperation.java
+++ b/core/java/android/content/SyncOperation.java
@@ -28,6 +28,7 @@ public class SyncOperation implements Comparable {
public final Account account;
public int syncSource;
public String authority;
+ public final boolean allowParallelSyncs;
public Bundle extras;
public final String key;
public long earliestRunTime;
@@ -38,10 +39,11 @@ public class SyncOperation implements Comparable {
public long effectiveRunTime;
public SyncOperation(Account account, int source, String authority, Bundle extras,
- long delayInMs, long backoff, long delayUntil) {
+ long delayInMs, long backoff, long delayUntil, boolean allowParallelSyncs) {
this.account = account;
this.syncSource = source;
this.authority = authority;
+ this.allowParallelSyncs = allowParallelSyncs;
this.extras = new Bundle(extras);
removeFalseExtra(ContentResolver.SYNC_EXTRAS_UPLOAD);
removeFalseExtra(ContentResolver.SYNC_EXTRAS_MANUAL);
@@ -80,6 +82,7 @@ public class SyncOperation implements Comparable {
this.earliestRunTime = SystemClock.elapsedRealtime();
this.backoff = other.backoff;
this.delayUntil = other.delayUntil;
+ this.allowParallelSyncs = other.allowParallelSyncs;
this.updateEffectiveRunTime();
this.key = toKey();
}
@@ -117,7 +120,7 @@ public class SyncOperation implements Comparable {
private String toKey() {
StringBuilder sb = new StringBuilder();
sb.append("authority: ").append(authority);
- sb.append(" account {name=" + account.name + ", type=" + account.type + "}");
+ sb.append(" account {name=" + account.name + ", type=" + account.type + "}");
sb.append(" extras: ");
extrasToStringBuilder(extras, sb);
return sb.toString();
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index f826147..bfdf4a1 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -18,6 +18,7 @@ package android.content;
import com.google.android.collect.Maps;
+import android.content.pm.RegisteredServicesCache;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.Pair;
@@ -41,7 +42,7 @@ public class SyncQueue {
// quick lookup of an enqueued SyncOperation.
public final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap();
- public SyncQueue(SyncStorageEngine syncStorageEngine) {
+ public SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters) {
mSyncStorageEngine = syncStorageEngine;
ArrayList<SyncStorageEngine.PendingOperation> ops
= mSyncStorageEngine.getPendingOperations();
@@ -49,10 +50,17 @@ public class SyncQueue {
for (int i=0; i<N; i++) {
SyncStorageEngine.PendingOperation op = ops.get(i);
final Pair<Long, Long> backoff = syncStorageEngine.getBackoff(op.account, op.authority);
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+ syncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(op.authority, op.account.type));
+ if (syncAdapterInfo == null) {
+ continue;
+ }
SyncOperation syncOperation = new SyncOperation(
op.account, op.syncSource, op.authority, op.extras, 0 /* delay */,
backoff != null ? backoff.first : 0,
- syncStorageEngine.getDelayUntilTime(op.account, op.authority));
+ syncStorageEngine.getDelayUntilTime(op.account, op.authority),
+ syncAdapterInfo.type.allowParallelSyncs());
syncOperation.expedited = op.expedited;
syncOperation.pendingOperation = op;
add(syncOperation, op);
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9aabfc4..6309256 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -48,7 +48,6 @@
theme does not set this value, meaning it is based on whether the
window is floating. -->
<attr name="backgroundDimEnabled" format="boolean" />
-
<!-- =========== -->
<!-- Text styles -->
<!-- =========== -->
@@ -105,7 +104,7 @@
<!-- Color of list item text in alert dialogs. -->
<attr name="textColorAlertDialogListItem" format="reference|color" />
-
+
<!-- Search widget more corpus result item background. -->
<attr name="searchWidgetCorpusItemBackground" format="reference|color" />
@@ -235,7 +234,7 @@
be a selector that uses state_accelerated to pick a non-solid
color when running on devices that can draw such a bitmap
with complex compositing on top at 60fps.
-
+
<p>There are a few special considerations to use when setting this
drawable:
<ul>
@@ -558,7 +557,7 @@
<!-- PopupWindow style to use for action modes when showing as a window overlay. -->
<attr name="actionModePopupWindowStyle" format="reference" />
-
+
<!-- =================== -->
<!-- Preference styles -->
<!-- =================== -->
@@ -4401,6 +4400,18 @@
<attr name="accountType"/>
<attr name="userVisible" format="boolean"/>
<attr name="supportsUploading" format="boolean"/>
+ <!-- Set to true to tell the SyncManager that this SyncAdapter supports
+ multiple simultaneous syncs for the same account type and authority.
+ Otherwise the SyncManager will be sure not to issue a start sync request
+ to this SyncAdapter if the SyncAdapter is already syncing another account.
+ Defaults to false.
+ -->
+ <attr name="allowParallelSyncs" format="boolean"/>
+ <!-- Set to true to tell the SyncManager to automatically call setIsSyncable(..., ..., 1)
+ for the SyncAdapter instead of issuaing an initialization sync to the SyncAdapter.
+ Defaults to false.
+ -->
+ <attr name="isAlwaysSyncable" format="boolean"/>
</declare-styleable>
<!-- =============================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 22fe535..95ffac2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1399,6 +1399,8 @@
<public type="attr" name="buttonBarButtonStyle" />
<public type="attr" name="segmentedButtonStyle" />
<public type="attr" name="staticWallpaperPreview" />
+ <public type="attr" name="allowParallelSyncs" />
+ <public type="attr" name="isAlwaysSyncable" />
<public type="anim" name="animator_fade_in" />
<public type="anim" name="animator_fade_out" />
@@ -1411,7 +1413,7 @@
<public type="anim" name="accelerate_quint_interpolator" />
<!-- Acceleration curve matching a quint ease out function. -->
<public type="anim" name="decelerate_quint_interpolator" />
-
+
<public type="id" name="home" />
<!-- Context menu ID for the "Select text..." menu item to switch to text
selection context mode in text views. -->
@@ -1569,5 +1571,4 @@
<public type="style" name="Holo.SegmentedButton" />
<public type="style" name="Holo.Light.SegmentedButton" />
<public type="string" name="selectTextMode" />
-
</resources>
diff --git a/core/tests/coretests/src/android/content/SyncOperationTest.java b/core/tests/coretests/src/android/content/SyncOperationTest.java
index 57435e5..37e948d 100644
--- a/core/tests/coretests/src/android/content/SyncOperationTest.java
+++ b/core/tests/coretests/src/android/content/SyncOperationTest.java
@@ -47,7 +47,8 @@ public class SyncOperationTest extends AndroidTestCase {
b1,
100,
1000,
- 10000);
+ 10000,
+ false);
// Same as op1 but different time infos
SyncOperation op2 = new SyncOperation(account1,
@@ -56,7 +57,8 @@ public class SyncOperationTest extends AndroidTestCase {
b1,
200,
2000,
- 20000);
+ 20000,
+ false);
// Same as op1 but different authority
SyncOperation op3 = new SyncOperation(account1,
@@ -65,7 +67,8 @@ public class SyncOperationTest extends AndroidTestCase {
b1,
100,
1000,
- 10000);
+ 10000,
+ false);
// Same as op1 but different account
SyncOperation op4 = new SyncOperation(account2,
@@ -74,7 +77,8 @@ public class SyncOperationTest extends AndroidTestCase {
b1,
100,
1000,
- 10000);
+ 10000,
+ false);
// Same as op1 but different bundle
SyncOperation op5 = new SyncOperation(account1,
@@ -83,7 +87,8 @@ public class SyncOperationTest extends AndroidTestCase {
b2,
100,
1000,
- 10000);
+ 10000,
+ false);
assertEquals(op1.key, op2.key);
assertNotSame(op1.key, op3.key);