Merge "Make a separate active sync queue for initialization and regular syncs."
This commit is contained in:
committed by
Android (Google) Code Review
commit
c8b7971cb3
File diff suppressed because it is too large
Load Diff
@@ -33,9 +33,12 @@ public class SyncOperation implements Comparable {
|
||||
public long earliestRunTime;
|
||||
public boolean expedited;
|
||||
public SyncStorageEngine.PendingOperation pendingOperation;
|
||||
public Long backoff;
|
||||
public long delayUntil;
|
||||
public long effectiveRunTime;
|
||||
|
||||
public SyncOperation(Account account, int source, String authority, Bundle extras,
|
||||
long delayInMs) {
|
||||
long delayInMs, long backoff, long delayUntil) {
|
||||
this.account = account;
|
||||
this.syncSource = source;
|
||||
this.authority = authority;
|
||||
@@ -48,6 +51,8 @@ public class SyncOperation implements Comparable {
|
||||
removeFalseExtra(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS);
|
||||
removeFalseExtra(ContentResolver.SYNC_EXTRAS_EXPEDITED);
|
||||
removeFalseExtra(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS);
|
||||
this.delayUntil = delayUntil;
|
||||
this.backoff = backoff;
|
||||
final long now = SystemClock.elapsedRealtime();
|
||||
if (delayInMs < 0) {
|
||||
this.expedited = true;
|
||||
@@ -56,6 +61,7 @@ public class SyncOperation implements Comparable {
|
||||
this.expedited = false;
|
||||
this.earliestRunTime = now + delayInMs;
|
||||
}
|
||||
updateEffectiveRunTime();
|
||||
this.key = toKey();
|
||||
}
|
||||
|
||||
@@ -72,49 +78,78 @@ public class SyncOperation implements Comparable {
|
||||
this.extras = new Bundle(other.extras);
|
||||
this.expedited = other.expedited;
|
||||
this.earliestRunTime = SystemClock.elapsedRealtime();
|
||||
this.backoff = other.backoff;
|
||||
this.delayUntil = other.delayUntil;
|
||||
this.updateEffectiveRunTime();
|
||||
this.key = toKey();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return dump(true);
|
||||
}
|
||||
|
||||
public String dump(boolean useOneLine) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("authority: ").append(authority);
|
||||
sb.append(" account: ").append(account);
|
||||
sb.append(" extras: ");
|
||||
extrasToStringBuilder(extras, sb, false /* asKey */);
|
||||
sb.append(" syncSource: ").append(syncSource);
|
||||
sb.append(" when: ").append(earliestRunTime);
|
||||
sb.append(" expedited: ").append(expedited);
|
||||
sb.append(account.name);
|
||||
sb.append(" (" + account.type + ")");
|
||||
sb.append(", " + authority);
|
||||
sb.append(", ");
|
||||
sb.append(SyncStorageEngine.SOURCES[syncSource]);
|
||||
sb.append(", earliestRunTime " + earliestRunTime);
|
||||
if (expedited) {
|
||||
sb.append(", EXPEDITED");
|
||||
}
|
||||
if (!useOneLine && !extras.keySet().isEmpty()) {
|
||||
sb.append("\n ");
|
||||
extrasToStringBuilder(extras, sb);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean isInitialization() {
|
||||
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
|
||||
}
|
||||
|
||||
public boolean ignoreBackoff() {
|
||||
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false);
|
||||
}
|
||||
|
||||
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, true /* asKey */);
|
||||
extrasToStringBuilder(extras, sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb, boolean asKey) {
|
||||
public static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
|
||||
sb.append("[");
|
||||
for (String key : bundle.keySet()) {
|
||||
// if we are writing this as a key don't consider whether this
|
||||
// is an initialization sync or not when computing the key since
|
||||
// we set this flag appropriately when dispatching the sync request.
|
||||
if (asKey && ContentResolver.SYNC_EXTRAS_INITIALIZE.equals(key)) {
|
||||
continue;
|
||||
}
|
||||
sb.append(key).append("=").append(bundle.get(key)).append(" ");
|
||||
}
|
||||
sb.append("]");
|
||||
}
|
||||
|
||||
public void updateEffectiveRunTime() {
|
||||
effectiveRunTime = ignoreBackoff()
|
||||
? earliestRunTime
|
||||
: Math.max(
|
||||
Math.max(earliestRunTime, delayUntil),
|
||||
backoff);
|
||||
}
|
||||
|
||||
public int compareTo(Object o) {
|
||||
SyncOperation other = (SyncOperation)o;
|
||||
if (earliestRunTime == other.earliestRunTime) {
|
||||
|
||||
if (expedited != other.expedited) {
|
||||
return expedited ? -1 : 1;
|
||||
}
|
||||
|
||||
if (effectiveRunTime == other.effectiveRunTime) {
|
||||
return 0;
|
||||
}
|
||||
return (earliestRunTime < other.earliestRunTime) ? -1 : 1;
|
||||
|
||||
return effectiveRunTime < other.effectiveRunTime ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,17 +16,18 @@
|
||||
|
||||
package android.content;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
import com.google.android.collect.Maps;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Pair;
|
||||
import android.util.Log;
|
||||
import android.accounts.Account;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -38,7 +39,7 @@ public class SyncQueue {
|
||||
|
||||
// A Map of SyncOperations operationKey -> SyncOperation that is designed for
|
||||
// quick lookup of an enqueued SyncOperation.
|
||||
private final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap();
|
||||
public final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap();
|
||||
|
||||
public SyncQueue(SyncStorageEngine syncStorageEngine) {
|
||||
mSyncStorageEngine = syncStorageEngine;
|
||||
@@ -47,8 +48,11 @@ public class SyncQueue {
|
||||
final int N = ops.size();
|
||||
for (int i=0; i<N; i++) {
|
||||
SyncStorageEngine.PendingOperation op = ops.get(i);
|
||||
final Pair<Long, Long> backoff = syncStorageEngine.getBackoff(op.account, op.authority);
|
||||
SyncOperation syncOperation = new SyncOperation(
|
||||
op.account, op.syncSource, op.authority, op.extras, 0 /* delay */);
|
||||
op.account, op.syncSource, op.authority, op.extras, 0 /* delay */,
|
||||
backoff != null ? backoff.first : 0,
|
||||
syncStorageEngine.getDelayUntilTime(op.account, op.authority));
|
||||
syncOperation.expedited = op.expedited;
|
||||
syncOperation.pendingOperation = op;
|
||||
add(syncOperation, op);
|
||||
@@ -119,65 +123,26 @@ public class SyncQueue {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the operation that should run next. Operations are sorted by their earliestRunTime,
|
||||
* prioritizing first those with a syncable state of "unknown" that aren't retries then
|
||||
* expedited operations.
|
||||
* The earliestRunTime is adjusted by the sync adapter's backoff and delayUntil times, if any.
|
||||
* @return the operation that should run next and when it should run. The time may be in
|
||||
* the future. It is expressed in milliseconds since boot.
|
||||
*/
|
||||
public Pair<SyncOperation, Long> nextOperation() {
|
||||
SyncOperation best = null;
|
||||
long bestRunTime = 0;
|
||||
boolean bestSyncableIsUnknownAndNotARetry = false;
|
||||
public void onBackoffChanged(Account account, String providerName, long backoff) {
|
||||
// for each op that matches the account and provider update its
|
||||
// backoff and effectiveStartTime
|
||||
for (SyncOperation op : mOperationsMap.values()) {
|
||||
long opRunTime = op.earliestRunTime;
|
||||
if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
|
||||
Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(op.account, op.authority);
|
||||
long delayUntil = mSyncStorageEngine.getDelayUntilTime(op.account, op.authority);
|
||||
opRunTime = Math.max(
|
||||
Math.max(opRunTime, delayUntil),
|
||||
backoff != null ? backoff.first : 0);
|
||||
}
|
||||
// we know a sync is a retry if the intialization flag is set, since that will only
|
||||
// be set by the sync dispatching code, thus if it is set it must have already been
|
||||
// dispatched
|
||||
final boolean syncableIsUnknownAndNotARetry =
|
||||
!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)
|
||||
&& mSyncStorageEngine.getIsSyncable(op.account, op.authority) < 0;
|
||||
// if the unsyncable state differs, make the current the best if it is unsyncable
|
||||
// else, if the expedited state differs, make the current the best if it is expedited
|
||||
// else, make the current the best if it is earlier than the best
|
||||
if (best == null
|
||||
|| ((bestSyncableIsUnknownAndNotARetry == syncableIsUnknownAndNotARetry)
|
||||
? (best.expedited == op.expedited
|
||||
? opRunTime < bestRunTime
|
||||
: op.expedited)
|
||||
: syncableIsUnknownAndNotARetry)) {
|
||||
best = op;
|
||||
bestSyncableIsUnknownAndNotARetry = syncableIsUnknownAndNotARetry;
|
||||
bestRunTime = opRunTime;
|
||||
if (op.account.equals(account) && op.authority.equals(providerName)) {
|
||||
op.backoff = backoff;
|
||||
op.updateEffectiveRunTime();
|
||||
}
|
||||
}
|
||||
if (best == null) {
|
||||
return null;
|
||||
}
|
||||
return Pair.create(best, bestRunTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the SyncOperation that should be run next and is ready to run.
|
||||
* @param now the current {@link android.os.SystemClock#elapsedRealtime()}, used to
|
||||
* decide if the sync operation is ready to run
|
||||
* @return the SyncOperation that should be run next and is ready to run.
|
||||
*/
|
||||
public Pair<SyncOperation, Long> nextReadyToRun(long now) {
|
||||
Pair<SyncOperation, Long> nextOpAndRunTime = nextOperation();
|
||||
if (nextOpAndRunTime == null || nextOpAndRunTime.second > now) {
|
||||
return null;
|
||||
public void onDelayUntilTimeChanged(Account account, String providerName, long delayUntil) {
|
||||
// for each op that matches the account and provider update its
|
||||
// delayUntilTime and effectiveStartTime
|
||||
for (SyncOperation op : mOperationsMap.values()) {
|
||||
if (op.account.equals(account) && op.authority.equals(providerName)) {
|
||||
op.delayUntil = delayUntil;
|
||||
op.updateEffectiveRunTime();
|
||||
}
|
||||
}
|
||||
return nextOpAndRunTime;
|
||||
}
|
||||
|
||||
public void remove(Account account, String authority) {
|
||||
@@ -200,9 +165,17 @@ public class SyncQueue {
|
||||
}
|
||||
|
||||
public void dump(StringBuilder sb) {
|
||||
final long now = SystemClock.elapsedRealtime();
|
||||
sb.append("SyncQueue: ").append(mOperationsMap.size()).append(" operation(s)\n");
|
||||
for (SyncOperation operation : mOperationsMap.values()) {
|
||||
sb.append(operation).append("\n");
|
||||
sb.append(" ");
|
||||
if (operation.effectiveRunTime <= now) {
|
||||
sb.append("READY");
|
||||
} else {
|
||||
sb.append(DateUtils.formatElapsedTime((operation.effectiveRunTime - now) / 1000));
|
||||
}
|
||||
sb.append(" - ");
|
||||
sb.append(operation.dump(false)).append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ public class SyncStorageEngine extends Handler {
|
||||
private final ArrayList<PendingOperation> mPendingOperations =
|
||||
new ArrayList<PendingOperation>();
|
||||
|
||||
private SyncInfo mCurrentSync;
|
||||
private final ArrayList<SyncInfo> mCurrentSyncs = new ArrayList<SyncInfo>();
|
||||
|
||||
private final SparseArray<SyncStatusInfo> mSyncStatus =
|
||||
new SparseArray<SyncStatusInfo>();
|
||||
@@ -690,23 +690,12 @@ public class SyncStorageEngine extends Handler {
|
||||
|
||||
/**
|
||||
* Returns true if there is currently a sync operation for the given
|
||||
* account or authority in the pending list, or actively being processed.
|
||||
* account or authority actively being processed.
|
||||
*/
|
||||
public boolean isSyncActive(Account account, String authority) {
|
||||
synchronized (mAuthorities) {
|
||||
int i = mPendingOperations.size();
|
||||
while (i > 0) {
|
||||
i--;
|
||||
// TODO(fredq): this probably shouldn't be considering
|
||||
// pending operations.
|
||||
PendingOperation op = mPendingOperations.get(i);
|
||||
if (op.account.equals(account) && op.authority.equals(authority)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mCurrentSync != null) {
|
||||
AuthorityInfo ainfo = getAuthority(mCurrentSync.authorityId);
|
||||
for (SyncInfo syncInfo : mCurrentSyncs) {
|
||||
AuthorityInfo ainfo = getAuthority(syncInfo.authorityId);
|
||||
if (ainfo != null && ainfo.account.equals(account)
|
||||
&& ainfo.authority.equals(authority)) {
|
||||
return true;
|
||||
@@ -887,40 +876,47 @@ public class SyncStorageEngine extends Handler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the currently active sync is changing (there can only be
|
||||
* one at a time). Either supply a valid ActiveSyncContext with information
|
||||
* about the sync, or null to stop the currently active sync.
|
||||
* Called when a sync is starting. Supply a valid ActiveSyncContext with information
|
||||
* about the sync.
|
||||
*/
|
||||
public void setActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
|
||||
public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) {
|
||||
final SyncInfo syncInfo;
|
||||
synchronized (mAuthorities) {
|
||||
if (activeSyncContext != null) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "setActiveSync: account="
|
||||
+ activeSyncContext.mSyncOperation.account
|
||||
+ " auth=" + activeSyncContext.mSyncOperation.authority
|
||||
+ " src=" + activeSyncContext.mSyncOperation.syncSource
|
||||
+ " extras=" + activeSyncContext.mSyncOperation.extras);
|
||||
}
|
||||
if (mCurrentSync != null) {
|
||||
Log.w(TAG, "setActiveSync called with existing active sync!");
|
||||
}
|
||||
AuthorityInfo authority = getAuthorityLocked(
|
||||
activeSyncContext.mSyncOperation.account,
|
||||
activeSyncContext.mSyncOperation.authority,
|
||||
"setActiveSync");
|
||||
if (authority == null) {
|
||||
return;
|
||||
}
|
||||
mCurrentSync = new SyncInfo(authority.ident,
|
||||
authority.account, authority.authority,
|
||||
activeSyncContext.mStartTime);
|
||||
} else {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "setActiveSync: null");
|
||||
mCurrentSync = null;
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "setActiveSync: account="
|
||||
+ activeSyncContext.mSyncOperation.account
|
||||
+ " auth=" + activeSyncContext.mSyncOperation.authority
|
||||
+ " src=" + activeSyncContext.mSyncOperation.syncSource
|
||||
+ " extras=" + activeSyncContext.mSyncOperation.extras);
|
||||
}
|
||||
AuthorityInfo authority = getOrCreateAuthorityLocked(
|
||||
activeSyncContext.mSyncOperation.account,
|
||||
activeSyncContext.mSyncOperation.authority,
|
||||
-1 /* assign a new identifier if creating a new authority */,
|
||||
true /* write to storage if this results in a change */);
|
||||
syncInfo = new SyncInfo(authority.ident,
|
||||
authority.account, authority.authority,
|
||||
activeSyncContext.mStartTime);
|
||||
mCurrentSyncs.add(syncInfo);
|
||||
}
|
||||
|
||||
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
|
||||
reportActiveChange();
|
||||
return syncInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to indicate that a previously active sync is no longer active.
|
||||
*/
|
||||
public void removeActiveSync(SyncInfo syncInfo) {
|
||||
synchronized (mAuthorities) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "removeActiveSync: account="
|
||||
+ syncInfo.account + " auth=" + syncInfo.authority);
|
||||
}
|
||||
mCurrentSyncs.remove(syncInfo);
|
||||
}
|
||||
|
||||
reportActiveChange();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1095,10 +1091,26 @@ public class SyncStorageEngine extends Handler {
|
||||
* Return the currently active sync information, or null if there is no
|
||||
* active sync. Note that the returned object is the real, live active
|
||||
* sync object, so be careful what you do with it.
|
||||
* <p>
|
||||
* Since multiple concurrent syncs are now supported you should use
|
||||
* {@link #getCurrentSyncs()} to get the accurate list of current syncs.
|
||||
* This method returns the first item from the list of current syncs
|
||||
* or null if there are none.
|
||||
* @deprecated use {@link #getCurrentSyncs()}
|
||||
*/
|
||||
public SyncInfo getCurrentSync() {
|
||||
synchronized (mAuthorities) {
|
||||
return mCurrentSync;
|
||||
return !mCurrentSyncs.isEmpty() ? mCurrentSyncs.get(0) : null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the currently active syncs. Note that the returned items are the
|
||||
* real, live active sync objects, so be careful what you do with it.
|
||||
*/
|
||||
public List<SyncInfo> getCurrentSyncs() {
|
||||
synchronized (mAuthorities) {
|
||||
return new ArrayList<SyncInfo>(mCurrentSyncs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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.test.AndroidTestCase;
|
||||
import android.test.RenamingDelegatingContext;
|
||||
import android.test.mock.MockContext;
|
||||
import android.test.mock.MockContentResolver;
|
||||
import android.accounts.Account;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
|
||||
public class SyncQueueTest extends AndroidTestCase {
|
||||
private static final Account ACCOUNT1 = new Account("test.account1", "test.type1");
|
||||
private static final Account ACCOUNT2 = new Account("test.account2", "test.type2");
|
||||
private static final String AUTHORITY1 = "test.authority1";
|
||||
private static final String AUTHORITY2 = "test.authority2";
|
||||
private static final String AUTHORITY3 = "test.authority3";
|
||||
|
||||
private SyncStorageEngine mSettings;
|
||||
private SyncQueue mSyncQueue;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
MockContentResolver mockResolver = new MockContentResolver();
|
||||
mSettings = SyncStorageEngine.newTestInstance(new TestContext(mockResolver, getContext()));
|
||||
mSyncQueue = new SyncQueue(mSettings);
|
||||
}
|
||||
|
||||
public void testSyncQueueOrder() throws Exception {
|
||||
final SyncOperation op1 = new SyncOperation(
|
||||
ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("1"), 0);
|
||||
final SyncOperation op2 = new SyncOperation(
|
||||
ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("2"), 100);
|
||||
final SyncOperation op3 = new SyncOperation(
|
||||
ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("3"), 150);
|
||||
final SyncOperation op4 = new SyncOperation(
|
||||
ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("4"), 60);
|
||||
final SyncOperation op5 = new SyncOperation(
|
||||
ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("5"), 80);
|
||||
final SyncOperation op6 = new SyncOperation(
|
||||
ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("6"), 0);
|
||||
op6.expedited = true;
|
||||
|
||||
mSyncQueue.add(op1);
|
||||
mSyncQueue.add(op2);
|
||||
mSyncQueue.add(op3);
|
||||
mSyncQueue.add(op4);
|
||||
mSyncQueue.add(op5);
|
||||
mSyncQueue.add(op6);
|
||||
|
||||
long now = SystemClock.elapsedRealtime() + 200;
|
||||
|
||||
assertEquals(op6, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op6);
|
||||
|
||||
assertEquals(op1, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op1);
|
||||
|
||||
assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op4);
|
||||
|
||||
assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op5);
|
||||
|
||||
assertEquals(op2, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op2);
|
||||
|
||||
assertEquals(op3, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op3);
|
||||
}
|
||||
|
||||
public void testOrderWithBackoff() throws Exception {
|
||||
final SyncOperation op1 = new SyncOperation(
|
||||
ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("1"), 0);
|
||||
final SyncOperation op2 = new SyncOperation(
|
||||
ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("2"), 100);
|
||||
final SyncOperation op3 = new SyncOperation(
|
||||
ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("3"), 150);
|
||||
final SyncOperation op4 = new SyncOperation(
|
||||
ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY3, newTestBundle("4"), 60);
|
||||
final SyncOperation op5 = new SyncOperation(
|
||||
ACCOUNT1, SyncStorageEngine.SOURCE_USER, AUTHORITY1, newTestBundle("5"), 80);
|
||||
final SyncOperation op6 = new SyncOperation(
|
||||
ACCOUNT2, SyncStorageEngine.SOURCE_USER, AUTHORITY2, newTestBundle("6"), 0);
|
||||
op6.expedited = true;
|
||||
|
||||
mSyncQueue.add(op1);
|
||||
mSyncQueue.add(op2);
|
||||
mSyncQueue.add(op3);
|
||||
mSyncQueue.add(op4);
|
||||
mSyncQueue.add(op5);
|
||||
mSyncQueue.add(op6);
|
||||
|
||||
long now = SystemClock.elapsedRealtime() + 200;
|
||||
|
||||
assertEquals(op6, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op6);
|
||||
|
||||
assertEquals(op1, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op1);
|
||||
|
||||
mSettings.setBackoff(ACCOUNT2, AUTHORITY3, now + 200, 5);
|
||||
assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
|
||||
|
||||
mSettings.setBackoff(ACCOUNT2, AUTHORITY3, SyncStorageEngine.NOT_IN_BACKOFF_MODE, 0);
|
||||
assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
|
||||
|
||||
mSettings.setDelayUntilTime(ACCOUNT2, AUTHORITY3, now + 200);
|
||||
assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
|
||||
|
||||
mSettings.setDelayUntilTime(ACCOUNT2, AUTHORITY3, 0);
|
||||
assertEquals(op4, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op4);
|
||||
|
||||
assertEquals(op5, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op5);
|
||||
|
||||
assertEquals(op2, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op2);
|
||||
|
||||
assertEquals(op3, mSyncQueue.nextReadyToRun(now).first);
|
||||
mSyncQueue.remove(op3);
|
||||
}
|
||||
|
||||
Bundle newTestBundle(String val) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("test", val);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
static class TestContext extends ContextWrapper {
|
||||
ContentResolver mResolver;
|
||||
|
||||
public TestContext(ContentResolver resolver, Context realContext) {
|
||||
super(new RenamingDelegatingContext(new MockContext(), realContext, "test."));
|
||||
mResolver = resolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enforceCallingOrSelfPermission(String permission, String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentResolver getContentResolver() {
|
||||
return mResolver;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user