SyncManager sync blow up fix
Duplicates for periodic syncs would be created just after boot and when a periodic sync was delayed. This CL fixes the behavior. Bug: 26954967 Change-Id: I2440961629d443a62f96360563d435af9137857b
This commit is contained in:
@ -96,6 +96,7 @@ import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -177,18 +178,6 @@ public class SyncManager {
|
||||
*/
|
||||
private static final int SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES = 10; // 10 bytes
|
||||
|
||||
/**
|
||||
* How long to delay each queued {@link SyncHandler} message that may have occurred before boot
|
||||
* or befor the device became provisioned.
|
||||
*/
|
||||
private static final long PER_SYNC_BOOT_DELAY_MILLIS = 1000L; // 1 second
|
||||
|
||||
/**
|
||||
* The maximum amount of time we're willing to delay syncs out of boot, after device has been
|
||||
* provisioned, etc.
|
||||
*/
|
||||
private static final long MAX_SYNC_BOOT_DELAY_MILLIS = 60000L; // 1 minute
|
||||
|
||||
/**
|
||||
* If a previously scheduled sync becomes ready and we are low on storage, it gets
|
||||
* pushed back for this amount of time.
|
||||
@ -242,12 +231,24 @@ public class SyncManager {
|
||||
private SparseArray<SyncOperation> mScheduledSyncs = new SparseArray<SyncOperation>(32);
|
||||
private final Random mRand;
|
||||
|
||||
private int getUnusedJobId() {
|
||||
synchronized (mScheduledSyncs) {
|
||||
int newJobId = mRand.nextInt(Integer.MAX_VALUE);
|
||||
while (mScheduledSyncs.indexOfKey(newJobId) >= 0) {
|
||||
newJobId = mRand.nextInt(Integer.MAX_VALUE);
|
||||
private boolean isJobIdInUseLockedH(int jobId) {
|
||||
if (mScheduledSyncs.indexOfKey(jobId) >= 0) {
|
||||
return true;
|
||||
}
|
||||
for (ActiveSyncContext asc: mActiveSyncContexts) {
|
||||
if (asc.mSyncOperation.jobId == jobId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getUnusedJobIdH() {
|
||||
synchronized (mScheduledSyncs) {
|
||||
int newJobId;
|
||||
do {
|
||||
newJobId = mRand.nextInt(Integer.MAX_VALUE);
|
||||
} while (isJobIdInUseLockedH(newJobId));
|
||||
return newJobId;
|
||||
}
|
||||
}
|
||||
@ -425,6 +426,35 @@ public class SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel all unnecessary jobs. This function will be run once after every boot.
|
||||
*/
|
||||
private void cleanupJobs() {
|
||||
// O(n^2) in number of jobs, so we run this on the background thread.
|
||||
mSyncHandler.postAtFrontOfQueue(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<SyncOperation> ops = getAllPendingSyncsFromCache();
|
||||
Set<String> cleanedKeys = new HashSet<String>();
|
||||
for (SyncOperation opx: ops) {
|
||||
if (cleanedKeys.contains(opx.key)) {
|
||||
continue;
|
||||
}
|
||||
cleanedKeys.add(opx.key);
|
||||
for (SyncOperation opy: ops) {
|
||||
if (opx == opy) {
|
||||
continue;
|
||||
}
|
||||
if (opx.key.equals(opy.key)) {
|
||||
removeSyncOperationFromCache(opy.jobId);
|
||||
mJobScheduler.cancel(opy.jobId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private synchronized void verifyJobScheduler() {
|
||||
if (mJobScheduler != null) {
|
||||
return;
|
||||
@ -449,6 +479,7 @@ public class SyncManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
cleanupJobs();
|
||||
}
|
||||
|
||||
private JobScheduler getJobScheduler() {
|
||||
@ -722,7 +753,6 @@ public class SyncManager {
|
||||
String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
|
||||
long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
|
||||
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
EndPoint ep = new EndPoint(requestedAccount,requestedAuthority, userId);
|
||||
if (extras == null) {
|
||||
extras = new Bundle();
|
||||
}
|
||||
@ -1197,7 +1227,18 @@ public class SyncManager {
|
||||
}
|
||||
|
||||
// Check if duplicate syncs are pending. If found, keep one with least expected run time.
|
||||
if (!syncOperation.isReasonPeriodic()) {
|
||||
if (!syncOperation.isPeriodic) {
|
||||
// Check currently running syncs
|
||||
for (ActiveSyncContext asc: mActiveSyncContexts) {
|
||||
if (asc.mSyncOperation.key.equals(syncOperation.key)) {
|
||||
if (isLoggable) {
|
||||
Log.v(TAG, "Duplicate sync is already running. Not scheduling "
|
||||
+ syncOperation);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int duplicatesCount = 0;
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
syncOperation.expectedRuntime = now + minDelay;
|
||||
@ -1240,14 +1281,16 @@ public class SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
syncOperation.jobId = getUnusedJobId();
|
||||
// Syncs that are re-scheduled shouldn't get a new job id.
|
||||
if (syncOperation.jobId == SyncOperation.NO_JOB_ID) {
|
||||
syncOperation.jobId = getUnusedJobIdH();
|
||||
}
|
||||
addSyncOperationToCache(syncOperation);
|
||||
|
||||
if (isLoggable) {
|
||||
Slog.v(TAG, "scheduling sync operation " + syncOperation.target.toString());
|
||||
Slog.v(TAG, "scheduling sync operation " + syncOperation.toString());
|
||||
}
|
||||
|
||||
// This is done to give preference to syncs that are not pushed back.
|
||||
int priority = syncOperation.findPriority();
|
||||
|
||||
final int networkType = syncOperation.isNotAllowedOnMetered() ?
|
||||
@ -1343,7 +1386,7 @@ public class SyncManager {
|
||||
Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
|
||||
+ "encountered an error: " + operation);
|
||||
}
|
||||
scheduleSyncOperationH(operation, 0 /* immediately */);
|
||||
scheduleSyncOperationH(operation);
|
||||
} else if (syncResult.tooManyRetries) {
|
||||
// If this sync aborted because the internal sync loop retried too many times then
|
||||
// don't reschedule. Otherwise we risk getting into a retry loop.
|
||||
@ -1357,7 +1400,7 @@ public class SyncManager {
|
||||
Log.d(TAG, "retrying sync operation because even though it had an error "
|
||||
+ "it achieved some success");
|
||||
}
|
||||
scheduleSyncOperationH(operation, 0 /* immediately */);
|
||||
scheduleSyncOperationH(operation);
|
||||
} else if (syncResult.syncAlreadyInProgress) {
|
||||
if (isLoggable) {
|
||||
Log.d(TAG, "retrying sync operation that failed because there was already a "
|
||||
@ -2207,7 +2250,8 @@ public class SyncManager {
|
||||
synchronized (this) {
|
||||
if (!mBootCompleted || !mProvisioned) {
|
||||
if (msg.what == MESSAGE_START_SYNC) {
|
||||
deferSyncH((SyncOperation) msg.obj, 60*1000 /* 1 minute */);
|
||||
SyncOperation op = (SyncOperation) msg.obj;
|
||||
addSyncOperationToCache(op);
|
||||
}
|
||||
// Need to copy the message bc looper will recycle it.
|
||||
Message m = Message.obtain(msg);
|
||||
@ -2272,21 +2316,24 @@ public class SyncManager {
|
||||
|
||||
case MESSAGE_STOP_SYNC:
|
||||
op = (SyncOperation) msg.obj;
|
||||
boolean reschedule = msg.arg1 != 0;
|
||||
boolean applyBackoff = msg.arg2 != 0;
|
||||
if (isLoggable) {
|
||||
Slog.v(TAG, "Stop sync received. Reschedule: " + reschedule
|
||||
+ "Backoff: " + applyBackoff);
|
||||
}
|
||||
if (applyBackoff) {
|
||||
increaseBackoffSetting(op.target);
|
||||
}
|
||||
if (reschedule) {
|
||||
scheduleSyncOperationH(op);
|
||||
Slog.v(TAG, "Stop sync received.");
|
||||
}
|
||||
ActiveSyncContext asc = findActiveSyncContextH(op.jobId);
|
||||
if (asc != null) {
|
||||
runSyncFinishedOrCanceledH(null /* no result */, asc);
|
||||
boolean reschedule = msg.arg1 != 0;
|
||||
boolean applyBackoff = msg.arg2 != 0;
|
||||
if (isLoggable) {
|
||||
Slog.v(TAG, "Stopping sync. Reschedule: " + reschedule
|
||||
+ "Backoff: " + applyBackoff);
|
||||
}
|
||||
if (applyBackoff) {
|
||||
increaseBackoffSetting(op.target);
|
||||
}
|
||||
if (reschedule) {
|
||||
deferStoppedSyncH(op, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2414,7 +2461,8 @@ public class SyncManager {
|
||||
|
||||
/**
|
||||
* Defer the specified SyncOperation by rescheduling it on the JobScheduler with some
|
||||
* delay.
|
||||
* delay. This is equivalent to a failure. If this is a periodic sync, a delayed one-off
|
||||
* sync will be scheduled.
|
||||
*/
|
||||
private void deferSyncH(SyncOperation op, long delay) {
|
||||
mSyncJobService.callJobFinished(op.jobId, false);
|
||||
@ -2426,14 +2474,22 @@ public class SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
/* Same as deferSyncH, but assumes that job is no longer running on JobScheduler. */
|
||||
private void deferStoppedSyncH(SyncOperation op, long delay) {
|
||||
if (op.isPeriodic) {
|
||||
scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
|
||||
} else {
|
||||
removeSyncOperationFromCache(op.jobId);
|
||||
scheduleSyncOperationH(op, delay);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel an active sync and reschedule it on the JobScheduler with some delay.
|
||||
*/
|
||||
private void deferActiveSyncH(ActiveSyncContext asc) {
|
||||
SyncOperation op = asc.mSyncOperation;
|
||||
|
||||
mSyncHandler.obtainMessage(MESSAGE_STOP_SYNC, 0 /* no reschedule */,
|
||||
0 /* no backoff */, op).sendToTarget();
|
||||
runSyncFinishedOrCanceledH(null, asc);
|
||||
deferSyncH(op, SYNC_DELAY_ON_CONFLICT);
|
||||
}
|
||||
|
||||
@ -2467,6 +2523,7 @@ public class SyncManager {
|
||||
// Check for adapter delays.
|
||||
if (isAdapterDelayed(op.target)) {
|
||||
deferSyncH(op, 0 /* No minimum delay */);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Remove SyncOperation entry from mScheduledSyncs cache for non periodic jobs.
|
||||
@ -2567,10 +2624,10 @@ public class SyncManager {
|
||||
Slog.v(TAG, "updating period " + syncOperation + " to " + pollFrequencyMillis
|
||||
+ " and flex to " + flexMillis);
|
||||
}
|
||||
removePeriodicSyncInternalH(syncOperation);
|
||||
syncOperation.periodMillis = pollFrequencyMillis;
|
||||
syncOperation.flexMillis = flexMillis;
|
||||
scheduleSyncOperationH(syncOperation);
|
||||
SyncOperation newOp = new SyncOperation(syncOperation, pollFrequencyMillis,
|
||||
flexMillis);
|
||||
newOp.jobId = syncOperation.jobId;
|
||||
scheduleSyncOperationH(newOp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2614,9 +2671,8 @@ public class SyncManager {
|
||||
SyncOperation op = new SyncOperation(target, syncAdapterInfo.uid,
|
||||
syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC,
|
||||
SyncStorageEngine.SOURCE_PERIODIC, extras,
|
||||
syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID);
|
||||
op.periodMillis = pollFrequencyMillis;
|
||||
op.flexMillis = flexMillis;
|
||||
syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID,
|
||||
pollFrequencyMillis, flexMillis);
|
||||
scheduleSyncOperationH(op);
|
||||
mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
|
||||
}
|
||||
@ -2816,10 +2872,19 @@ public class SyncManager {
|
||||
* Should be called when a one-off instance of a periodic sync completes successfully.
|
||||
*/
|
||||
private void reschedulePeriodicSyncH(SyncOperation syncOperation) {
|
||||
removeSyncOperationFromCache(syncOperation.sourcePeriodicId);
|
||||
getJobScheduler().cancel(syncOperation.sourcePeriodicId);
|
||||
SyncOperation periodic = syncOperation.createPeriodicSyncOperation();
|
||||
scheduleSyncOperationH(periodic);
|
||||
// Ensure that the periodic sync wasn't removed.
|
||||
SyncOperation periodicSync = null;
|
||||
List<SyncOperation> ops = getAllPendingSyncsFromCache();
|
||||
for (SyncOperation op: ops) {
|
||||
if (op.isPeriodic && syncOperation.matchesPeriodicOperation(op)) {
|
||||
periodicSync = op;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (periodicSync == null) {
|
||||
return;
|
||||
}
|
||||
scheduleSyncOperationH(periodicSync);
|
||||
}
|
||||
|
||||
private void runSyncFinishedOrCanceledH(SyncResult syncResult,
|
||||
|
@ -79,9 +79,9 @@ public class SyncOperation {
|
||||
public final String key;
|
||||
|
||||
/** Poll frequency of periodic sync in milliseconds */
|
||||
public long periodMillis;
|
||||
public final long periodMillis;
|
||||
/** Flex time of periodic sync in milliseconds */
|
||||
public long flexMillis;
|
||||
public final long flexMillis;
|
||||
/** Descriptive string key for this operation */
|
||||
public String wakeLockName;
|
||||
/**
|
||||
@ -90,6 +90,9 @@ public class SyncOperation {
|
||||
*/
|
||||
public long expectedRuntime;
|
||||
|
||||
/** Stores the number of times this sync operation failed and had to be retried. */
|
||||
int retries;
|
||||
|
||||
/** jobId of the JobScheduler job corresponding to this sync */
|
||||
public int jobId;
|
||||
|
||||
@ -103,23 +106,32 @@ public class SyncOperation {
|
||||
private SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
|
||||
int reason, int source, Bundle extras, boolean allowParallelSyncs) {
|
||||
this(info, owningUid, owningPackage, reason, source, extras, allowParallelSyncs, false,
|
||||
NO_JOB_ID);
|
||||
NO_JOB_ID, 0, 0);
|
||||
}
|
||||
|
||||
public SyncOperation(SyncOperation op, long periodMillis, long flexMillis) {
|
||||
this(op.target, op.owningUid, op.owningPackage, op.reason, op.syncSource,
|
||||
new Bundle(op.extras), op.allowParallelSyncs, op.isPeriodic, op.sourcePeriodicId,
|
||||
periodMillis, flexMillis);
|
||||
}
|
||||
|
||||
public SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
|
||||
int reason, int source, Bundle extras, boolean allowParallelSyncs,
|
||||
boolean isPeriodic, int sourcePeriodicId) {
|
||||
boolean isPeriodic, int sourcePeriodicId, long periodMillis,
|
||||
long flexMillis) {
|
||||
this.target = info;
|
||||
this.owningUid = owningUid;
|
||||
this.owningPackage = owningPackage;
|
||||
this.reason = reason;
|
||||
this.syncSource = source;
|
||||
this.extras = new Bundle(extras);
|
||||
cleanBundle(this.extras);
|
||||
this.allowParallelSyncs = allowParallelSyncs;
|
||||
this.isPeriodic = isPeriodic;
|
||||
this.sourcePeriodicId = sourcePeriodicId;
|
||||
this.key = toKey(target, extras);
|
||||
this.periodMillis = periodMillis;
|
||||
this.flexMillis = flexMillis;
|
||||
this.jobId = NO_JOB_ID;
|
||||
this.key = toKey();
|
||||
}
|
||||
|
||||
/* Get a one off sync operation instance from a periodic sync. */
|
||||
@ -128,18 +140,8 @@ public class SyncOperation {
|
||||
return null;
|
||||
}
|
||||
SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, syncSource,
|
||||
new Bundle(extras), allowParallelSyncs, false, jobId /* sourcePeriodicId */);
|
||||
// Copied to help us recreate the periodic sync from this one off sync.
|
||||
op.periodMillis = periodMillis;
|
||||
op.flexMillis = flexMillis;
|
||||
return op;
|
||||
}
|
||||
|
||||
public SyncOperation createPeriodicSyncOperation() {
|
||||
SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, syncSource,
|
||||
new Bundle(extras), allowParallelSyncs, true, NO_JOB_ID);
|
||||
op.periodMillis = periodMillis;
|
||||
op.flexMillis = flexMillis;
|
||||
new Bundle(extras), allowParallelSyncs, false, jobId /* sourcePeriodicId */,
|
||||
periodMillis, flexMillis);
|
||||
return op;
|
||||
}
|
||||
|
||||
@ -224,6 +226,7 @@ public class SyncOperation {
|
||||
jobInfoExtras.putLong("periodMillis", periodMillis);
|
||||
jobInfoExtras.putLong("flexMillis", flexMillis);
|
||||
jobInfoExtras.putLong("expectedRuntime", expectedRuntime);
|
||||
jobInfoExtras.putInt("retries", retries);
|
||||
return jobInfoExtras;
|
||||
}
|
||||
|
||||
@ -240,6 +243,7 @@ public class SyncOperation {
|
||||
int initiatedBy;
|
||||
Bundle extras;
|
||||
boolean allowParallelSyncs, isPeriodic;
|
||||
long periodMillis, flexMillis;
|
||||
|
||||
if (!jobExtras.getBoolean("SyncManagerJob", false)) {
|
||||
return null;
|
||||
@ -256,6 +260,8 @@ public class SyncOperation {
|
||||
allowParallelSyncs = jobExtras.getBoolean("allowParallelSyncs", false);
|
||||
isPeriodic = jobExtras.getBoolean("isPeriodic", false);
|
||||
initiatedBy = jobExtras.getInt("sourcePeriodicId", NO_JOB_ID);
|
||||
periodMillis = jobExtras.getLong("periodMillis");
|
||||
flexMillis = jobExtras.getLong("flexMillis");
|
||||
extras = new Bundle();
|
||||
|
||||
PersistableBundle syncExtras = jobExtras.getPersistableBundle("syncExtras");
|
||||
@ -277,37 +283,13 @@ public class SyncOperation {
|
||||
SyncStorageEngine.EndPoint target =
|
||||
new SyncStorageEngine.EndPoint(account, provider, userId);
|
||||
SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, source,
|
||||
extras, allowParallelSyncs, isPeriodic, initiatedBy);
|
||||
extras, allowParallelSyncs, isPeriodic, initiatedBy, periodMillis, flexMillis);
|
||||
op.jobId = jobExtras.getInt("jobId");
|
||||
op.periodMillis = jobExtras.getLong("periodMillis");
|
||||
op.flexMillis = jobExtras.getLong("flexMillis");
|
||||
op.expectedRuntime = jobExtras.getLong("expectedRuntime");
|
||||
op.retries = jobExtras.getInt("retries");
|
||||
return op;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the bundle attached to this SyncOperation doesn't have unnecessary
|
||||
* flags set.
|
||||
* @param bundle to clean.
|
||||
*/
|
||||
private void cleanBundle(Bundle bundle) {
|
||||
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_UPLOAD);
|
||||
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_MANUAL);
|
||||
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS);
|
||||
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
|
||||
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY);
|
||||
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS);
|
||||
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_EXPEDITED);
|
||||
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS);
|
||||
removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISALLOW_METERED);
|
||||
}
|
||||
|
||||
private void removeFalseExtra(Bundle bundle, String extraName) {
|
||||
if (!bundle.getBoolean(extraName, false)) {
|
||||
bundle.remove(extraName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether if this sync operation is running, the provided operation would conflict
|
||||
* with it.
|
||||
@ -326,6 +308,12 @@ public class SyncOperation {
|
||||
return reason == REASON_PERIODIC;
|
||||
}
|
||||
|
||||
boolean matchesPeriodicOperation(SyncOperation other) {
|
||||
return target.matchesSpec(other.target)
|
||||
&& SyncManager.syncExtrasEquals(extras, other.extras, true)
|
||||
&& periodMillis == other.periodMillis && flexMillis == other.flexMillis;
|
||||
}
|
||||
|
||||
boolean isDerivedFromFailedPeriodicSync() {
|
||||
return sourcePeriodicId != NO_JOB_ID;
|
||||
}
|
||||
@ -339,15 +327,18 @@ public class SyncOperation {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static String toKey(SyncStorageEngine.EndPoint info, Bundle extras) {
|
||||
private String toKey() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("provider: ").append(info.provider);
|
||||
sb.append(" account {name=" + info.account.name
|
||||
sb.append("provider: ").append(target.provider);
|
||||
sb.append(" account {name=" + target.account.name
|
||||
+ ", user="
|
||||
+ info.userId
|
||||
+ target.userId
|
||||
+ ", type="
|
||||
+ info.account.type
|
||||
+ target.account.type
|
||||
+ "}");
|
||||
sb.append(" isPeriodic: ").append(isPeriodic);
|
||||
sb.append(" period: ").append(periodMillis);
|
||||
sb.append(" flex: ").append(flexMillis);
|
||||
sb.append(" extras: ");
|
||||
extrasToStringBuilder(extras, sb);
|
||||
return sb.toString();
|
||||
@ -360,7 +351,9 @@ public class SyncOperation {
|
||||
|
||||
String dump(PackageManager pm, boolean useOneLine) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(target.account.name)
|
||||
sb.append("JobId: ").append(jobId)
|
||||
.append(", ")
|
||||
.append(target.account.name)
|
||||
.append(" u")
|
||||
.append(target.userId).append(" (")
|
||||
.append(target.account.type)
|
||||
|
@ -145,14 +145,10 @@ public class SyncOperationTest extends AndroidTestCase {
|
||||
"provider", 0);
|
||||
Bundle extras = new Bundle();
|
||||
SyncOperation periodic = new SyncOperation(ep, 0, "package", 0, 0, extras, false, true,
|
||||
SyncOperation.NO_JOB_ID);
|
||||
periodic.periodMillis = 60000;
|
||||
periodic.flexMillis = 10000;
|
||||
SyncOperation.NO_JOB_ID, 60000, 10000);
|
||||
SyncOperation oneoff = periodic.createOneTimeSyncOperation();
|
||||
assertFalse("Conversion to oneoff sync failed.", oneoff.isPeriodic);
|
||||
SyncOperation recreated = oneoff.createPeriodicSyncOperation();
|
||||
assertTrue("Conversion to periodic sync failed.", oneoff.isPeriodic);
|
||||
assertEquals("Period not restored", periodic.periodMillis, recreated.periodMillis);
|
||||
assertEquals("Flex not restored", periodic.flexMillis, recreated.flexMillis);
|
||||
assertEquals("Period not restored", periodic.periodMillis, oneoff.periodMillis);
|
||||
assertEquals("Flex not restored", periodic.flexMillis, oneoff.flexMillis);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user