SyncManager on JobScheduler
SyncManager now uses JobScheduler for scheduling
and persistence purposes.
Change-Id: I38c92aedbf4d891ca297644d0b706835aaedfcd6
diff --git a/api/current.txt b/api/current.txt
index ebeaefc..5681438 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7852,6 +7852,7 @@
field public static final java.lang.String SYNC_EXTRAS_INITIALIZE = "initialize";
field public static final java.lang.String SYNC_EXTRAS_MANUAL = "force";
field public static final java.lang.String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
+ field public static final java.lang.String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging";
field public static final java.lang.String SYNC_EXTRAS_UPLOAD = "upload";
field public static final int SYNC_OBSERVER_TYPE_ACTIVE = 4; // 0x4
field public static final int SYNC_OBSERVER_TYPE_PENDING = 2; // 0x2
@@ -9036,6 +9037,7 @@
method public android.content.SyncRequest.Builder setIgnoreSettings(boolean);
method public android.content.SyncRequest.Builder setManual(boolean);
method public android.content.SyncRequest.Builder setNoRetry(boolean);
+ method public android.content.SyncRequest.Builder setRequiresCharging(boolean);
method public android.content.SyncRequest.Builder setSyncAdapter(android.accounts.Account, java.lang.String);
method public android.content.SyncRequest.Builder syncOnce();
method public android.content.SyncRequest.Builder syncPeriodic(long, long);
diff --git a/api/system-current.txt b/api/system-current.txt
index 07e30a8..63e3a21 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8140,6 +8140,7 @@
field public static final java.lang.String SYNC_EXTRAS_INITIALIZE = "initialize";
field public static final java.lang.String SYNC_EXTRAS_MANUAL = "force";
field public static final java.lang.String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
+ field public static final java.lang.String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging";
field public static final java.lang.String SYNC_EXTRAS_UPLOAD = "upload";
field public static final int SYNC_OBSERVER_TYPE_ACTIVE = 4; // 0x4
field public static final int SYNC_OBSERVER_TYPE_PENDING = 2; // 0x2
@@ -9350,6 +9351,7 @@
method public android.content.SyncRequest.Builder setIgnoreSettings(boolean);
method public android.content.SyncRequest.Builder setManual(boolean);
method public android.content.SyncRequest.Builder setNoRetry(boolean);
+ method public android.content.SyncRequest.Builder setRequiresCharging(boolean);
method public android.content.SyncRequest.Builder setSyncAdapter(android.accounts.Account, java.lang.String);
method public android.content.SyncRequest.Builder syncOnce();
method public android.content.SyncRequest.Builder syncPeriodic(long, long);
diff --git a/api/test-current.txt b/api/test-current.txt
index ddb7dc9..69ee311 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -7855,6 +7855,7 @@
field public static final java.lang.String SYNC_EXTRAS_INITIALIZE = "initialize";
field public static final java.lang.String SYNC_EXTRAS_MANUAL = "force";
field public static final java.lang.String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
+ field public static final java.lang.String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging";
field public static final java.lang.String SYNC_EXTRAS_UPLOAD = "upload";
field public static final int SYNC_OBSERVER_TYPE_ACTIVE = 4; // 0x4
field public static final int SYNC_OBSERVER_TYPE_PENDING = 2; // 0x2
@@ -9041,6 +9042,7 @@
method public android.content.SyncRequest.Builder setIgnoreSettings(boolean);
method public android.content.SyncRequest.Builder setManual(boolean);
method public android.content.SyncRequest.Builder setNoRetry(boolean);
+ method public android.content.SyncRequest.Builder setRequiresCharging(boolean);
method public android.content.SyncRequest.Builder setSyncAdapter(android.accounts.Account, java.lang.String);
method public android.content.SyncRequest.Builder syncOnce();
method public android.content.SyncRequest.Builder syncPeriodic(long, long);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 3461c11..0172408 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -89,6 +89,13 @@
public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
/**
+ * If this extra is set to true, the sync request will be scheduled
+ * only when the device is plugged in. This is equivalent to calling
+ * setRequiresCharging(true) on {@link SyncRequest}.
+ */
+ public static final String SYNC_EXTRAS_REQUIRE_CHARGING = "require_charging";
+
+ /**
* @deprecated instead use
* {@link #SYNC_EXTRAS_MANUAL}
*/
diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java
index 7619c6d..4f7d20d 100644
--- a/core/java/android/content/SyncRequest.java
+++ b/core/java/android/content/SyncRequest.java
@@ -246,6 +246,10 @@
* this sync is bound to a provider), otherwise null.
*/
private String mAuthority;
+ /**
+ * Whether the sync requires the phone to be plugged in.
+ */
+ private boolean mRequiresCharging;
public Builder() {
}
@@ -335,13 +339,22 @@
public Builder setDisallowMetered(boolean disallow) {
if (mIgnoreSettings && disallow) {
throw new IllegalArgumentException("setDisallowMetered(true) after having"
- + "specified that settings are ignored.");
+ + " specified that settings are ignored.");
}
mDisallowMetered = disallow;
return this;
}
/**
+ * Specify whether the sync requires the phone to be plugged in.
+ * @param requiresCharging true if sync requires the phone to be plugged in. Default false.
+ */
+ public Builder setRequiresCharging(boolean requiresCharging) {
+ mRequiresCharging = true;
+ return this;
+ }
+
+ /**
* Specify an authority and account for this transfer.
*
* @param authority A String identifying the content provider to be synced.
@@ -499,6 +512,9 @@
if (mDisallowMetered) {
mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, true);
}
+ if (mRequiresCharging) {
+ mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING, true);
+ }
if (mIgnoreSettings) {
mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4ab81e9..72a9f98 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3131,6 +3131,10 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+ <service android:name="com.android.server.content.SyncJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
<service
android:name="com.android.server.pm.BackgroundDexOptService"
android:exported="true"
diff --git a/services/core/java/com/android/server/content/AppIdleMonitor.java b/services/core/java/com/android/server/content/AppIdleMonitor.java
deleted file mode 100644
index 2d768d8..0000000
--- a/services/core/java/com/android/server/content/AppIdleMonitor.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2015 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 com.android.server.content;
-
-import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
-import android.os.UserHandle;
-
-import com.android.server.LocalServices;
-
-/**
- * Helper to listen for app idle and charging status changes and restart backed off
- * sync operations.
- */
-class AppIdleMonitor extends AppIdleStateChangeListener {
-
- private final SyncManager mSyncManager;
- private final UsageStatsManagerInternal mUsageStats;
- private boolean mAppIdleParoleOn;
-
- AppIdleMonitor(SyncManager syncManager) {
- mSyncManager = syncManager;
- mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
- mAppIdleParoleOn = mUsageStats.isAppIdleParoleOn();
-
- mUsageStats.addAppIdleStateChangeListener(this);
- }
-
- void setAppIdleParoleOn(boolean appIdleParoleOn) {
- if (mAppIdleParoleOn == appIdleParoleOn) {
- return;
- }
- mAppIdleParoleOn = appIdleParoleOn;
- if (mAppIdleParoleOn) {
- mSyncManager.onAppNotIdle(null, UserHandle.USER_ALL);
- }
- }
-
- boolean isAppIdle(String packageName, int uidForAppId, int userId) {
- return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, uidForAppId, userId);
- }
-
- @Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
- // Don't care if the app is becoming idle
- if (idle) return;
- mSyncManager.onAppNotIdle(packageName, userId);
- }
-
- @Override
- public void onParoleStateChanged(boolean isParoleOn) {
- setAppIdleParoleOn(isParoleOn);
- }
-}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index f72b1c3..1968c8c 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -126,7 +126,7 @@
for (int i=0; i<sorted.size(); i++) {
int pid = sorted.get(i);
pw.print(" pid "); pw.print(pid); pw.print(": ");
- pw.print(pidCounts.get(pid)); pw.println(" observers");
+ pw.print(pidCounts.get(pid)); pw.println(" observers");
}
pw.println();
pw.print(" Total number of nodes: "); pw.println(counts[0]);
@@ -162,11 +162,11 @@
PackageManagerInternal.class);
packageManagerInternal.setSyncAdapterPackagesprovider(
new PackageManagerInternal.SyncAdapterPackagesProvider() {
- @Override
- public String[] getPackages(String authority, int userId) {
- return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
- }
- });
+ @Override
+ public String[] getPackages(String authority, int userId) {
+ return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
+ }
+ });
}
public void systemReady() {
@@ -183,7 +183,7 @@
*/
@Override
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
- IContentObserver observer, int userHandle) {
+ IContentObserver observer, int userHandle) {
if (observer == null || uri == null) {
throw new IllegalArgumentException("You must pass a valid uri and observer");
}
@@ -218,7 +218,7 @@
}
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
- IContentObserver observer) {
+ IContentObserver observer) {
registerContentObserver(uri, notifyForDescendants, observer,
UserHandle.getCallingUserId());
}
@@ -243,8 +243,8 @@
*/
@Override
public void notifyChange(Uri uri, IContentObserver observer,
- boolean observerWantsSelfNotifications, boolean syncToNetwork,
- int userHandle) {
+ boolean observerWantsSelfNotifications, boolean syncToNetwork,
+ int userHandle) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
+ " from observer " + observer + ", syncToNetwork " + syncToNetwork);
@@ -318,7 +318,7 @@
}
public void notifyChange(Uri uri, IContentObserver observer,
- boolean observerWantsSelfNotifications, boolean syncToNetwork) {
+ boolean observerWantsSelfNotifications, boolean syncToNetwork) {
notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
UserHandle.getCallingUserId());
}
@@ -406,8 +406,8 @@
runAtTime = 60;
}
// Schedule periodic sync.
- getSyncManager().getSyncStorageEngine()
- .updateOrAddPeriodicSync(info, runAtTime, flextime, extras);
+ getSyncManager().updateOrAddPeriodicSync(info, runAtTime,
+ flextime, extras);
} else {
long beforeRuntimeMillis = (flextime) * 1000;
long runtimeMillis = runAtTime * 1000;
@@ -450,7 +450,7 @@
*/
@Override
public void cancelSyncAsUser(Account account, String authority, ComponentName cname,
- int userId) {
+ int userId) {
if (authority != null && authority.length() == 0) {
throw new IllegalArgumentException("Authority must be non-empty");
}
@@ -459,15 +459,15 @@
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
+ if (cname != null) {
+ Slog.e(TAG, "cname not null.");
+ return;
+ }
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
SyncStorageEngine.EndPoint info;
- if (cname == null) {
- info = new SyncStorageEngine.EndPoint(account, authority, userId);
- } else {
- info = new SyncStorageEngine.EndPoint(cname, userId, -1);
- }
+ info = new SyncStorageEngine.EndPoint(account, authority, userId);
syncManager.clearScheduledSyncOperations(info);
syncManager.cancelActiveSync(info, null /* all syncs for this adapter */);
}
@@ -492,7 +492,7 @@
// Remove periodic sync.
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
- getSyncManager().getSyncStorageEngine().removePeriodicSync(info, extras);
+ getSyncManager().removePeriodicSync(info, extras);
}
// Cancel active syncs and clear pending syncs from the queue.
syncManager.cancelScheduledSyncOperation(info, extras);
@@ -585,7 +585,7 @@
@Override
public void setSyncAutomaticallyAsUser(Account account, String providerName, boolean sync,
- int userId) {
+ int userId) {
if (TextUtils.isEmpty(providerName)) {
throw new IllegalArgumentException("Authority must be non-empty");
}
@@ -606,10 +606,10 @@
}
}
- /** Old API. Schedule periodic sync with default flex time. */
+ /** Old API. Schedule periodic sync with default flexMillis time. */
@Override
public void addPeriodicSync(Account account, String authority, Bundle extras,
- long pollFrequency) {
+ long pollFrequency) {
if (account == null) {
throw new IllegalArgumentException("Account must not be null");
}
@@ -631,11 +631,8 @@
try {
SyncStorageEngine.EndPoint info =
new SyncStorageEngine.EndPoint(account, authority, userId);
- getSyncManager().getSyncStorageEngine()
- .updateOrAddPeriodicSync(info,
- pollFrequency,
- defaultFlex,
- extras);
+ getSyncManager().updateOrAddPeriodicSync(info, pollFrequency,
+ defaultFlex, extras);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -654,10 +651,10 @@
int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
- getSyncManager().getSyncStorageEngine()
- .removePeriodicSync(
- new SyncStorageEngine.EndPoint(account, authority, userId),
- extras);
+ getSyncManager()
+ .removePeriodicSync(
+ new SyncStorageEngine.EndPoint(account, authority, userId),
+ extras);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -665,7 +662,7 @@
public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName,
- ComponentName cname) {
+ ComponentName cname) {
if (account == null) {
throw new IllegalArgumentException("Account must not be null");
}
@@ -678,7 +675,7 @@
int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
- return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
+ return getSyncManager().getPeriodicSyncs(
new SyncStorageEngine.EndPoint(account, providerName, userId));
} finally {
restoreCallingIdentity(identityToken);
@@ -836,7 +833,7 @@
* INTERACT_ACROSS_USERS_FULL permission.
*/
public SyncStatusInfo getSyncStatusAsUser(Account account, String authority,
- ComponentName cname, int userId) {
+ ComponentName cname, int userId) {
if (TextUtils.isEmpty(authority)) {
throw new IllegalArgumentException("Authority must not be empty");
}
@@ -871,7 +868,7 @@
@Override
public boolean isSyncPendingAsUser(Account account, String authority, ComponentName cname,
- int userId) {
+ int userId) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
enforceCrossUserPermission(userId,
@@ -954,7 +951,7 @@
private final Object observersLock;
public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
- int _uid, int _pid, int _userHandle) {
+ int _uid, int _pid, int _userHandle) {
this.observersLock = observersLock;
observer = o;
uid = _uid;
@@ -975,14 +972,14 @@
}
public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- String name, String prefix, SparseIntArray pidCounts) {
+ String name, String prefix, SparseIntArray pidCounts) {
pidCounts.put(pid, pidCounts.get(pid)+1);
pw.print(prefix); pw.print(name); pw.print(": pid=");
- pw.print(pid); pw.print(" uid=");
- pw.print(uid); pw.print(" user=");
- pw.print(userHandle); pw.print(" target=");
- pw.println(Integer.toHexString(System.identityHashCode(
- observer != null ? observer.asBinder() : null)));
+ pw.print(pid); pw.print(" uid=");
+ pw.print(uid); pw.print(" user=");
+ pw.print(userHandle); pw.print(" target=");
+ pw.println(Integer.toHexString(System.identityHashCode(
+ observer != null ? observer.asBinder() : null)));
}
}
@@ -999,7 +996,7 @@
}
public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
- String name, String prefix, int[] counts, SparseIntArray pidCounts) {
+ String name, String prefix, int[] counts, SparseIntArray pidCounts) {
String innerName = null;
if (mObservers.size() > 0) {
if ("".equals(name)) {
@@ -1050,15 +1047,15 @@
// Invariant: userHandle is either a hard user number or is USER_ALL
public void addObserverLocked(Uri uri, IContentObserver observer,
- boolean notifyForDescendants, Object observersLock,
- int uid, int pid, int userHandle) {
+ boolean notifyForDescendants, Object observersLock,
+ int uid, int pid, int userHandle) {
addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
uid, pid, userHandle);
}
private void addObserverLocked(Uri uri, int index, IContentObserver observer,
- boolean notifyForDescendants, Object observersLock,
- int uid, int pid, int userHandle) {
+ boolean notifyForDescendants, Object observersLock,
+ int uid, int pid, int userHandle) {
// If this is the leaf node add the observer
if (index == countUriSegments(uri)) {
mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
@@ -1118,8 +1115,8 @@
}
private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
- boolean observerWantsSelfNotifications, int targetUserHandle,
- ArrayList<ObserverCall> calls) {
+ boolean observerWantsSelfNotifications, int targetUserHandle,
+ ArrayList<ObserverCall> calls) {
int N = mObservers.size();
IBinder observerBinder = observer == null ? null : observer.asBinder();
for (int i = 0; i < N; i++) {
@@ -1148,8 +1145,8 @@
* targetUserHandle is either a hard user handle or is USER_ALL
*/
public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
- boolean observerWantsSelfNotifications, int targetUserHandle,
- ArrayList<ObserverCall> calls) {
+ boolean observerWantsSelfNotifications, int targetUserHandle,
+ ArrayList<ObserverCall> calls) {
String segment = null;
int segmentCount = countUriSegments(uri);
if (index >= segmentCount) {
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
new file mode 100644
index 0000000..a621d73
--- /dev/null
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 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 com.android.server.content;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.Intent;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+public class SyncJobService extends JobService {
+ private static final String TAG = "SyncManager";
+
+ public static final String EXTRA_MESSENGER = "messenger";
+
+ private Messenger mMessenger;
+ private SparseArray<JobParameters> jobParamsMap = new SparseArray<JobParameters>();
+
+ /**
+ * This service is started by the SyncManager which passes a messenger object to
+ * communicate back with it. It never stops while the device is running.
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ mMessenger = intent.getParcelableExtra(EXTRA_MESSENGER);
+ Message m = Message.obtain();
+ m.what = SyncManager.SyncHandler.MESSAGE_JOBSERVICE_OBJECT;
+ m.obj = this;
+ sendMessage(m);
+
+ return START_NOT_STICKY;
+ }
+
+ private void sendMessage(Message message) {
+ if (mMessenger == null) {
+ Slog.e(TAG, "Messenger not initialized.");
+ return;
+ }
+ try {
+ mMessenger.send(message);
+ } catch (RemoteException e) {
+ Slog.e(TAG, e.toString());
+ }
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+ synchronized (jobParamsMap) {
+ jobParamsMap.put(params.getJobId(), params);
+ }
+ Message m = Message.obtain();
+ m.what = SyncManager.SyncHandler.MESSAGE_START_SYNC;
+ SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
+ if (op == null) {
+ Slog.e(TAG, "Got invalid job " + params.getJobId());
+ return false;
+ }
+ if (isLoggable) {
+ Slog.v(TAG, "Got start job message " + op.target);
+ }
+ m.obj = op;
+ sendMessage(m);
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "onStopJob called " + params.getJobId() + ", reason: "
+ + params.getStopReason());
+ }
+
+ synchronized (jobParamsMap) {
+ jobParamsMap.remove(params.getJobId());
+ }
+ Message m = Message.obtain();
+ m.what = SyncManager.SyncHandler.MESSAGE_STOP_SYNC;
+ m.obj = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
+ if (m.obj == null) {
+ return false;
+ }
+
+ // Reschedule if this job was NOT explicitly canceled.
+ m.arg1 = params.getStopReason() != JobParameters.REASON_CANCELED ? 1 : 0;
+ // Apply backoff only if stop is called due to timeout.
+ m.arg2 = params.getStopReason() == JobParameters.REASON_TIMEOUT ? 1 : 0;
+
+ sendMessage(m);
+ return false;
+ }
+
+ public void callJobFinished(int jobId, boolean needsReschedule) {
+ synchronized (jobParamsMap) {
+ JobParameters params = jobParamsMap.get(jobId);
+ if (params != null) {
+ jobFinished(params, needsReschedule);
+ jobParamsMap.remove(jobId);
+ } else {
+ Slog.e(TAG, "Job params not found for " + String.valueOf(jobId));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 2eb9095..91d4c9e 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -19,23 +19,20 @@
import android.accounts.Account;
import android.accounts.AccountAndUser;
import android.accounts.AccountManager;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
-import android.app.AlarmManager;
import android.app.AppGlobals;
-import android.app.IUidObserver;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ISyncAdapter;
import android.content.ISyncContext;
-import android.content.ISyncServiceAdapter;
-import android.content.ISyncStatusObserver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.PeriodicSync;
@@ -73,47 +70,70 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.WorkSource;
+import android.os.Messenger;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.text.format.Time;
-import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
import android.util.Pair;
-import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.DeviceIdleController;
-import com.android.server.LocalServices;
import com.android.server.accounts.AccountManagerService;
import com.android.server.content.SyncStorageEngine.AuthorityInfo;
import com.android.server.content.SyncStorageEngine.EndPoint;
import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
-import com.google.android.collect.Sets;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Random;
+import java.util.List;
+import java.util.HashSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
import java.util.Map;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.Objects;
-import java.util.Random;
-import java.util.Set;
/**
+ * Implementation details:
+ * All scheduled syncs will be passed on to JobScheduler as jobs
+ * (See {@link #scheduleSyncOperationH(SyncOperation, long)}. This function schedules a job
+ * with JobScheduler with appropriate delay and constraints (according to backoffs and extras).
+ * A local copy of each scheduled SyncOperation object is stored in {@link mScheduledSyncs}.This
+ * acts as a cache, so that we don't have to query JobScheduler every time we want to get a list of
+ * all scheduled operations. The scheduleSyncOperationH function also assigns a unique jobId to each
+ * SyncOperation.
+ *
+ * Periodic Syncs:
+ * Each periodic sync is scheduled as a periodic job. If a periodic sync fails, we create a new
+ * one off SyncOperation and set its {@link SyncOperation#sourcePeriodicId} field to the jobId of the
+ * periodic sync. We don't allow the periodic job to run while any job initiated by it is pending.
+ *
+ * Backoffs:
+ * Each {@link EndPoint} has a backoff associated with it. When a SyncOperation fails, we increase
+ * the backoff on the authority. Then we reschedule all syncs associated with that authority to
+ * run at a later time. Similarly, when a sync succeeds, backoff is cleared and all associated syncs
+ * are rescheduled. A rescheduled sync will get a new jobId.
+ *
+ * State of {@link mScheduledSyncs}:
+ * Every one-off SyncOperation will be put into this SparseArray when it is scheduled with
+ * JobScheduler. And it will be removed once JobScheduler has started the job. Periodic syncs work
+ * differently. They will always be present in mScheduledSyncs until the periodic sync is removed.
+ * This is to ensure that if a request to add a periodic sync comes in, we add a new one only if a
+ * duplicate doesn't exist. At every point of time, mScheduledSyncs and JobScheduler will show the
+ * same pending syncs.
+ *
* @hide
*/
public class SyncManager {
@@ -122,30 +142,11 @@
/** Delay a sync due to local changes this long. In milliseconds */
private static final long LOCAL_SYNC_DELAY;
- /**
- * If a sync takes longer than this and the sync queue is not empty then we will
- * cancel it and add it back to the end of the sync queue. In milliseconds.
- */
- private static final long MAX_TIME_PER_SYNC;
-
static {
- final boolean isLargeRAM = !ActivityManager.isLowRamDeviceStatic();
- int defaultMaxInitSyncs = isLargeRAM ? 5 : 2;
- int defaultMaxRegularSyncs = isLargeRAM ? 2 : 1;
- MAX_SIMULTANEOUS_INITIALIZATION_SYNCS =
- SystemProperties.getInt("sync.max_init_syncs", defaultMaxInitSyncs);
- MAX_SIMULTANEOUS_REGULAR_SYNCS =
- SystemProperties.getInt("sync.max_regular_syncs", defaultMaxRegularSyncs);
LOCAL_SYNC_DELAY =
SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */);
- MAX_TIME_PER_SYNC =
- SystemProperties.getLong("sync.max_time_per_sync", 5 * 60 * 1000 /* 5 minutes */);
- SYNC_NOTIFICATION_DELAY =
- SystemProperties.getLong("sync.notification_delay", 30 * 1000 /* 30 seconds */);
}
- private static final long SYNC_NOTIFICATION_DELAY;
-
/**
* When retrying a sync for the first time use this delay. After that
* the retry time will double until it reached MAX_SYNC_RETRY_TIME.
@@ -164,11 +165,6 @@
private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10;
/**
- * How long to wait before considering an active sync to have timed-out, and cancelling it.
- */
- private static final long ACTIVE_SYNC_TIMEOUT_MILLIS = 30L * 60 * 1000; // 30 mins
-
- /**
* How often to periodically poll network traffic for an adapter performing a sync to determine
* whether progress is being made.
*/
@@ -185,21 +181,30 @@
* 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 = 3000L; // 3 seconds
+ 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 = 120000L; // 2 minutes
+ 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.
+ */
+ private static final long SYNC_DELAY_ON_LOW_STORAGE = 60*60*1000; // 1 hour
+
+ /**
+ * If a sync becomes ready and it conflicts with an already running sync, it gets
+ * pushed back for this amount of time.
+ */
+ private static final long SYNC_DELAY_ON_CONFLICT = 10*1000; // 10 seconds
private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
- private static final int MAX_SIMULTANEOUS_REGULAR_SYNCS;
- private static final int MAX_SIMULTANEOUS_INITIALIZATION_SYNCS;
-
private Context mContext;
private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
@@ -215,30 +220,59 @@
volatile private boolean mReportedSyncActive = false;
private final NotificationManager mNotificationMgr;
- private AlarmManager mAlarmService = null;
private final IBatteryStats mBatteryStats;
+ private JobScheduler mJobScheduler;
+ private SyncJobService mSyncJobService;
private SyncStorageEngine mSyncStorageEngine;
- @GuardedBy("mSyncQueue")
- private final SyncQueue mSyncQueue;
-
protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
- // set if the sync active indicator should be reported
- private boolean mNeedSyncActiveNotification = false;
-
- private final PendingIntent mSyncAlarmIntent;
// Synchronized on "this". Instead of using this directly one should instead call
// its accessor, getConnManager().
private ConnectivityManager mConnManagerDoNotUseDirectly;
/** Track whether the device has already been provisioned. */
- private boolean mProvisioned;
+ private volatile boolean mProvisioned;
protected SyncAdaptersCache mSyncAdapters;
- private final AppIdleMonitor mAppIdleMonitor;
+ // Cache of all operations scheduled on the JobScheduler so that JobScheduler doesn't have
+ // to be queried often.
+ 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);
+ }
+ return newJobId;
+ }
+ }
+
+ private void addSyncOperationToCache(SyncOperation op) {
+ synchronized (mScheduledSyncs) {
+ mScheduledSyncs.put(op.jobId, op);
+ }
+ }
+
+ private void removeSyncOperationFromCache(int jobId) {
+ synchronized (mScheduledSyncs) {
+ mScheduledSyncs.remove(jobId);
+ }
+ }
+
+ private List<SyncOperation> getAllPendingSyncsFromCache() {
+ synchronized (mScheduledSyncs) {
+ List<SyncOperation> pending = new ArrayList<SyncOperation>(mScheduledSyncs.size());
+ for (int i=0; i<mScheduledSyncs.size(); i++) {
+ pending.add(mScheduledSyncs.valueAt(i));
+ }
+ return pending;
+ }
+ }
private final BroadcastReceiver mStorageIntentReceiver =
new BroadcastReceiver() {
@@ -247,7 +281,7 @@
String action = intent.getAction();
if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Internal storage is low.");
+ Slog.v(TAG, "Internal storage is low.");
}
mStorageIsLow = true;
cancelActiveSync(
@@ -255,39 +289,20 @@
null /* any sync */);
} else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Internal storage is ok.");
+ Slog.v(TAG, "Internal storage is ok.");
}
mStorageIsLow = false;
- sendCheckAlarmsMessage();
+ rescheduleSyncs(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL);
}
}
};
- private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
- @Override public void onReceive(Context context, Intent intent) {
- boolean idle = mPowerManager.isDeviceIdleMode()
- || mPowerManager.isLightDeviceIdleMode();
- mDeviceIsIdle = idle;
- if (idle) {
- cancelActiveSync(
- SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL,
- null /* any sync */);
- } else {
- if (mLocalDeviceIdleController != null) {
- if (!mReportedSyncActive) {
- mReportedSyncActive = true;
- mLocalDeviceIdleController.setSyncActive(true);
- }
- }
- sendCheckAlarmsMessage();
- }
- }
- };
-
private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mBootCompleted = true;
+ // Called because it gets all pending jobs and stores them in mScheduledSyncs cache.
+ verifyJobScheduler();
mSyncHandler.onBootCompleted();
}
};
@@ -295,39 +310,15 @@
private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- updateRunningAccounts();
-
- // Kick off sync for everyone, since this was a radical account change
- scheduleSync(null, UserHandle.USER_ALL, SyncOperation.REASON_ACCOUNTS_UPDATED, null,
- null, 0 /* no delay */, 0/* no delay */, false);
- }
- };
-
- private final IUidObserver mUidObserver = new IUidObserver.Stub() {
- @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
- }
-
- @Override public void onUidGone(int uid) throws RemoteException {
- }
-
- @Override public void onUidActive(int uid) throws RemoteException {
- }
-
- @Override public void onUidIdle(int uid) throws RemoteException {
- cancelSyncsForUid(uid);
+ updateRunningAccounts(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL
+ /* sync all targets */);
}
};
private final PowerManager mPowerManager;
- DeviceIdleController.LocalService mLocalDeviceIdleController;
-
- // Use this as a random offset to seed all periodic syncs.
- private int mSyncRandomOffsetMillis;
private final UserManager mUserManager;
- private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds
-
private List<UserInfo> getAllUsers() {
return mUserManager.getUsers();
}
@@ -344,10 +335,13 @@
return found;
}
- public void updateRunningAccounts() {
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_ACCOUNTS_UPDATED");
+ /** target indicates endpoints that should be synced after account info is updated. */
+ private void updateRunningAccounts(EndPoint target) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_ACCOUNTS_UPDATED");
// Update accounts in handler thread.
- mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_ACCOUNTS_UPDATED);
+ Message m = mSyncHandler.obtainMessage(SyncHandler.MESSAGE_ACCOUNTS_UPDATED);
+ m.obj = target;
+ m.sendToTarget();
}
private void doDatabaseCleanup() {
@@ -356,32 +350,35 @@
if (user.partial) continue;
Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(
user.id, mContext.getOpPackageName());
+
mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
}
}
private BroadcastReceiver mConnectivityIntentReceiver =
new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final boolean wasConnected = mDataConnectionIsConnected;
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final boolean wasConnected = mDataConnectionIsConnected;
- // don't use the intent to figure out if network is connected, just check
- // ConnectivityManager directly.
- mDataConnectionIsConnected = readDataConnectionState();
- if (mDataConnectionIsConnected) {
- if (!wasConnected) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Reconnection detected: clearing all backoffs");
- }
- synchronized (mSyncQueue) {
- mSyncStorageEngine.clearAllBackoffsLocked(mSyncQueue);
+ // Don't use the intent to figure out if network is connected, just check
+ // ConnectivityManager directly.
+ mDataConnectionIsConnected = readDataConnectionState();
+ if (mDataConnectionIsConnected) {
+ if (!wasConnected) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "Reconnection detected: clearing all backoffs");
+ }
+ }
+ clearAllBackoffs();
}
}
- sendCheckAlarmsMessage();
- }
- }
- };
+ };
+
+ private void clearAllBackoffs() {
+ mSyncStorageEngine.clearAllBackoffsLocked();
+ rescheduleSyncs(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL);
+ }
private boolean readDataConnectionState() {
NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo();
@@ -390,12 +387,12 @@
private BroadcastReceiver mShutdownIntentReceiver =
new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.w(TAG, "Writing sync state before shutdown...");
- getSyncStorageEngine().writeAllState();
- }
- };
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.w(TAG, "Writing sync state before shutdown...");
+ getSyncStorageEngine().writeAllState();
+ }
+ };
private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
@Override
@@ -414,7 +411,6 @@
}
};
- private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
private final SyncHandler mSyncHandler;
private volatile boolean mBootCompleted = false;
@@ -429,6 +425,37 @@
}
}
+ private synchronized void verifyJobScheduler() {
+ if (mJobScheduler != null) {
+ return;
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "initializing JobScheduler object.");
+ }
+ mJobScheduler = (JobScheduler) mContext.getSystemService(
+ Context.JOB_SCHEDULER_SERVICE);
+ // Get all persisted syncs from JobScheduler
+ List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();
+ synchronized (mScheduledSyncs) {
+ for (JobInfo job : pendingJobs) {
+ SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
+ if (op != null) {
+ mScheduledSyncs.put(op.jobId, op);
+ if (!op.isPeriodic) {
+ // Set the pending status of this EndPoint to true. Pending icon is
+ // shown on the settings activity.
+ mSyncStorageEngine.markPending(op.target, true);
+ }
+ }
+ }
+ }
+ }
+
+ private JobScheduler getJobScheduler() {
+ verifyJobScheduler();
+ return mJobScheduler;
+ }
+
/**
* Should only be created after {@link ContentService#systemReady()} so that
* {@link PackageManager} is ready to query.
@@ -443,21 +470,30 @@
mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
@Override
public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) {
- if (info.target_provider) {
- scheduleSync(info.account, info.userId, reason, info.provider, extras,
- 0 /* no flex */,
+ scheduleSync(info.account, info.userId, reason, info.provider, extras,
+ 0 /* no flexMillis */,
0 /* run immediately */,
false);
- } else if (info.target_service) {
- scheduleSync(info.service, info.userId, reason, extras,
- 0 /* no flex */,
- 0 /* run immediately */);
- }
+ }
+ });
+
+ mSyncStorageEngine.setPeriodicSyncAddedListener(
+ new SyncStorageEngine.PeriodicSyncAddedListener() {
+ @Override
+ public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
+ long flex) {
+ updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
+ }
+ });
+
+ mSyncStorageEngine.setOnAuthorityRemovedListener(new SyncStorageEngine.OnAuthorityRemovedListener() {
+ @Override
+ public void onAuthorityRemoved(EndPoint removedAuthority) {
+ removeSyncsForAuthority(removedAuthority);
}
});
mSyncAdapters = new SyncAdaptersCache(mContext);
- mSyncQueue = new SyncQueue(mContext.getPackageManager(), mSyncStorageEngine, mSyncAdapters);
mSyncHandler = new SyncHandler(BackgroundThread.get().getLooper());
@@ -473,10 +509,7 @@
}
}, mSyncHandler);
- mSyncAlarmIntent = PendingIntent.getBroadcast(
- mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);
-
- mAppIdleMonitor = new AppIdleMonitor(this);
+ mRand = new Random(System.currentTimeMillis());
IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
@@ -491,10 +524,6 @@
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
context.registerReceiver(mStorageIntentReceiver, intentFilter);
- intentFilter = new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
- intentFilter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
- context.registerReceiver(mDeviceIdleReceiver, intentFilter);
-
intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
intentFilter.setPriority(100);
context.registerReceiver(mShutdownIntentReceiver, intentFilter);
@@ -506,18 +535,9 @@
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
- try {
- ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
- ActivityManager.UID_OBSERVER_IDLE);
- } catch (RemoteException e) {
- // ignored; both services live in system_server
- }
-
if (!factoryTest) {
mNotificationMgr = (NotificationManager)
- context.getSystemService(Context.NOTIFICATION_SERVICE);
- context.registerReceiver(new SyncAlarmIntentReceiver(),
- new IntentFilter(ACTION_SYNC_ALARM));
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
} else {
mNotificationMgr = null;
}
@@ -543,15 +563,6 @@
SYNC_LOOP_WAKE_LOCK);
mSyncManagerWakeLock.setReferenceCounted(false);
- mSyncStorageEngine.addStatusChangeListener(
- ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() {
- @Override
- public void onStatusChanged(int which) {
- // force the sync loop to run if the settings change
- sendCheckAlarmsMessage();
- }
- });
-
mProvisioned = isDeviceProvisioned();
if (!mProvisioned) {
final ContentResolver resolver = context.getContentResolver();
@@ -589,8 +600,9 @@
null, null);
}
- // Pick a random second in a day to seed all periodic syncs
- mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000;
+ Intent startServiceIntent = new Intent(mContext, SyncJobService.class);
+ startServiceIntent.putExtra(SyncJobService.EXTRA_MESSENGER, new Messenger(mSyncHandler));
+ mContext.startService(startServiceIntent);
}
private boolean isDeviceProvisioned() {
@@ -619,22 +631,22 @@
int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName);
UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
- // If it's not a restricted user, return isSyncable
+ // If it's not a restricted user, return isSyncable.
if (userInfo == null || !userInfo.isRestricted()) return isSyncable;
- // Else check if the sync adapter has opted-in or not
+ // Else check if the sync adapter has opted-in or not.
RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(providerName, account.type), userId);
+ SyncAdapterType.newKey(providerName, account.type), userId);
if (syncAdapterInfo == null) return isSyncable;
PackageInfo pInfo = null;
try {
pInfo = AppGlobals.getPackageManager().getPackageInfo(
- syncAdapterInfo.componentName.getPackageName(), 0, userId);
+ syncAdapterInfo.componentName.getPackageName(), 0, userId);
if (pInfo == null) return isSyncable;
} catch (RemoteException re) {
- // Shouldn't happen
+ // Shouldn't happen.
return isSyncable;
}
if (pInfo.restrictedAccountType != null
@@ -645,97 +657,15 @@
}
}
- private void ensureAlarmService() {
- if (mAlarmService == null) {
- mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- }
- }
-
- /**
- * Initiate a sync using the new anonymous service API.
- * @param cname SyncService component bound to in order to perform the sync.
- * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL,
- * then all users' accounts are considered.
- * @param uid Linux uid of the application that is performing the sync.
- * @param extras a Map of SyncAdapter-specific information to control
- * syncs of a specific provider. Cannot be null.
- * @param beforeRunTimeMillis milliseconds before <code>runtimeMillis</code> that this sync may
- * be run.
- * @param runtimeMillis milliseconds from now by which this sync must be run.
- */
- public void scheduleSync(ComponentName cname, int userId, int uid, Bundle extras,
- long beforeRunTimeMillis, long runtimeMillis) {
- boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
- if (isLoggable) {
- Log.d(TAG, "one off sync for: " + cname + " " + extras.toString());
- }
-
- final android.content.pm.ServiceInfo sinfo;
- try {
- sinfo = mContext.getPackageManager().getServiceInfo(cname, userId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Not scheduling sync " + cname
- + " -- can't find service for user " + userId);
- return;
- }
- final int sUid = sinfo.applicationInfo.uid;
-
- try {
- if (ActivityManagerNative.getDefault().getAppStartMode(sUid, cname.getPackageName())
- == ActivityManager.APP_START_MODE_DISABLED) {
- Slog.w(TAG, "Not scheduling sync " + sUid + ":" + cname
- + " -- package not allowed to start");
+ private void setAuthorityPendingState(EndPoint info) {
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ for (SyncOperation op: ops) {
+ if (!op.isPeriodic && op.target.matchesSpec(info)) {
+ getSyncStorageEngine().markPending(info, true);
return;
}
- } catch (RemoteException e) {
}
-
- Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
- if (expedited) {
- runtimeMillis = -1; // this means schedule at the front of the queue
- }
-
- final boolean ignoreSettings =
- extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
- int source = SyncStorageEngine.SOURCE_SERVICE;
- boolean isEnabled = mSyncStorageEngine.getIsTargetServiceActive(cname, userId);
- // Only schedule this sync if
- // - we've explicitly been told to ignore settings.
- // - global sync is enabled for this user.
- boolean syncAllowed =
- ignoreSettings
- || mSyncStorageEngine.getMasterSyncAutomatically(userId);
- if (!syncAllowed) {
- if (isLoggable) {
- Log.d(TAG, "scheduleSync: sync of " + cname + " not allowed, dropping request.");
- }
- return;
- }
- if (!isEnabled) {
- if (isLoggable) {
- Log.d(TAG, "scheduleSync: " + cname + " is not enabled, dropping request");
- }
- return;
- }
- SyncStorageEngine.EndPoint info = new SyncStorageEngine.EndPoint(cname, userId, sUid);
- Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
- long delayUntil = mSyncStorageEngine.getDelayUntilTime(info);
- final long backoffTime = backoff != null ? backoff.first : 0;
- if (isLoggable) {
- Log.v(TAG, "schedule Sync:"
- + ", delay until " + delayUntil
- + ", run by " + runtimeMillis
- + ", flex " + beforeRunTimeMillis
- + ", source " + source
- + ", sync service " + cname
- + ", extras " + extras);
- }
- scheduleSyncOperation(
- new SyncOperation(cname, userId, sUid, cname.getPackageName(), uid, source, extras,
- runtimeMillis /* runtime */,
- beforeRunTimeMillis /* flextime */,
- backoffTime,
- delayUntil));
+ getSyncStorageEngine().markPending(info, false);
}
/**
@@ -780,10 +710,10 @@
* @param onlyThoseWithUnkownSyncableState Only sync authorities that have unknown state.
*/
public void scheduleSync(Account requestedAccount, int userId, int reason,
- String requestedAuthority, Bundle extras, long beforeRuntimeMillis,
- long runtimeMillis, boolean onlyThoseWithUnkownSyncableState) {
- boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
-
+ 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();
}
@@ -791,10 +721,6 @@
Log.d(TAG, "one-time sync for: " + requestedAccount + " " + extras.toString() + " "
+ requestedAuthority);
}
- Boolean expedited = extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
- if (expedited) {
- runtimeMillis = -1; // this means schedule at the front of the queue
- }
AccountAndUser[] accounts;
if (requestedAccount != null && userId != UserHandle.USER_ALL) {
@@ -803,7 +729,7 @@
accounts = mRunningAccounts;
if (accounts.length == 0) {
if (isLoggable) {
- Log.v(TAG, "scheduleSync: no accounts configured, dropping");
+ Slog.v(TAG, "scheduleSync: no accounts configured, dropping");
}
return;
}
@@ -826,8 +752,8 @@
} else if (requestedAuthority == null) {
source = SyncStorageEngine.SOURCE_POLL;
} else {
- // this isn't strictly server, since arbitrary callers can (and do) request
- // a non-forced two-way sync on a specific url
+ // This isn't strictly server, since arbitrary callers can (and do) request
+ // a non-forced two-way sync on a specific url.
source = SyncStorageEngine.SOURCE_SERVER;
}
@@ -845,9 +771,9 @@
syncableAuthorities.add(syncAdapter.type.authority);
}
- // if the url was specified then replace the list of authorities
+ // If the url was specified then replace the list of authorities
// with just this authority or clear it if this authority isn't
- // syncable
+ // syncable.
if (requestedAuthority != null) {
final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
syncableAuthorities.clear();
@@ -874,7 +800,7 @@
Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":"
+ syncAdapterInfo.componentName
+ " -- package not allowed to start");
- return;
+ continue;
}
} catch (RemoteException e) {
}
@@ -893,11 +819,11 @@
}
boolean syncAllowed =
- (isSyncable < 0) // always allow if the isSyncable state is unknown
- || ignoreSettings
- || (mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
+ (isSyncable < 0) // Always allow if the isSyncable state is unknown.
+ || ignoreSettings
+ || (mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
&& mSyncStorageEngine.getSyncAutomatically(account.account,
- account.userId, authority));
+ account.userId, authority));
if (!syncAllowed) {
if (isLoggable) {
Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
@@ -908,51 +834,98 @@
SyncStorageEngine.EndPoint info =
new SyncStorageEngine.EndPoint(
account.account, authority, account.userId);
- Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
long delayUntil =
mSyncStorageEngine.getDelayUntilTime(info);
- final long backoffTime = backoff != null ? backoff.first : 0;
if (isSyncable < 0) {
// Initialisation sync.
Bundle newExtras = new Bundle();
newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
if (isLoggable) {
- Log.v(TAG, "schedule initialisation Sync:"
+ Slog.v(TAG, "schedule initialisation Sync:"
+ ", delay until " + delayUntil
+ ", run by " + 0
- + ", flex " + 0
+ + ", flexMillis " + 0
+ ", source " + source
+ ", account " + account
+ ", authority " + authority
+ ", extras " + newExtras);
}
- scheduleSyncOperation(
+ postScheduleSyncMessage(
new SyncOperation(account.account, account.userId,
owningUid, owningPackage, reason, source,
- authority, newExtras, 0 /* immediate */, 0 /* No flex time*/,
- backoffTime, delayUntil, allowParallelSyncs));
+ authority, newExtras, allowParallelSyncs)
+ );
}
if (!onlyThoseWithUnkownSyncableState) {
if (isLoggable) {
- Log.v(TAG, "scheduleSync:"
+ Slog.v(TAG, "scheduleSync:"
+ " delay until " + delayUntil
+ " run by " + runtimeMillis
- + " flex " + beforeRuntimeMillis
+ + " flexMillis " + beforeRuntimeMillis
+ ", source " + source
+ ", account " + account
+ ", authority " + authority
+ ", extras " + extras);
}
- scheduleSyncOperation(
+ postScheduleSyncMessage(
new SyncOperation(account.account, account.userId,
owningUid, owningPackage, reason, source,
- authority, extras, runtimeMillis, beforeRuntimeMillis,
- backoffTime, delayUntil, allowParallelSyncs));
+ authority, extras, allowParallelSyncs)
+ );
}
}
}
}
+ private void removeSyncsForAuthority(EndPoint info) {
+ verifyJobScheduler();
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ for (SyncOperation op: ops) {
+ if (op.target.matchesSpec(info)) {
+ removeSyncOperationFromCache(op.jobId);
+ getJobScheduler().cancel(op.jobId);
+ }
+ }
+ }
+
+ /**
+ * Remove a specific periodic sync identified by its target and extras.
+ */
+ public void removePeriodicSync(EndPoint target, Bundle extras) {
+ Message m = mSyncHandler.obtainMessage(mSyncHandler.MESSAGE_REMOVE_PERIODIC_SYNC, target);
+ m.setData(extras);
+ m.sendToTarget();
+ }
+
+ /**
+ * Add a periodic sync. If a sync with same target and extras exists, its period and
+ * flexMillis will be updated.
+ */
+ public void updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex,
+ Bundle extras) {
+ UpdatePeriodicSyncMessagePayload payload = new UpdatePeriodicSyncMessagePayload(target,
+ pollFrequency, flex, extras);
+ mSyncHandler.obtainMessage(SyncHandler.MESSAGE_UPDATE_PERIODIC_SYNC, payload)
+ .sendToTarget();
+ }
+
+ /**
+ * Get a list of periodic syncs corresponding to the given target.
+ */
+ public List<PeriodicSync> getPeriodicSyncs(EndPoint target) {
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ List<PeriodicSync> periodicSyncs = new ArrayList<PeriodicSync>();
+
+ for (SyncOperation op: ops) {
+ if (op.isPeriodic && op.target.matchesSpec(target)) {
+ periodicSyncs.add(new PeriodicSync(op.target.account, op.target.provider,
+ op.extras, op.periodMillis / 1000, op.flexMillis / 1000));
+ }
+ }
+
+ return periodicSyncs;
+ }
+
/**
* Schedule sync based on local changes to a provider. Occurs within interval
* [LOCAL_SYNC_DELAY, 2*LOCAL_SYNC_DELAY].
@@ -982,28 +955,17 @@
return mSyncAdapters.getSyncAdapterPackagesForAuthority(authority, userId);
}
- private void sendSyncAlarmMessage() {
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_ALARM");
- mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_SYNC_ALARM);
- }
-
- private void sendCheckAlarmsMessage() {
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CHECK_ALARMS");
- mSyncHandler.removeMessages(SyncHandler.MESSAGE_CHECK_ALARMS);
- mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_CHECK_ALARMS);
- }
-
private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
- SyncResult syncResult) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_FINISHED");
+ SyncResult syncResult) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED");
Message msg = mSyncHandler.obtainMessage();
msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
- msg.obj = new SyncHandlerMessagePayload(syncContext, syncResult);
+ msg.obj = new SyncFinishedOrCancelledMessagePayload(syncContext, syncResult);
mSyncHandler.sendMessage(msg);
}
private void sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL");
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_CANCEL");
Message msg = mSyncHandler.obtainMessage();
msg.what = SyncHandler.MESSAGE_CANCEL;
msg.setData(extras);
@@ -1012,27 +974,12 @@
}
/**
- * Post a delayed message to the handler that will result in the cancellation of the provided
- * running sync's context.
- */
- private void postSyncExpiryMessage(ActiveSyncContext activeSyncContext) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "posting MESSAGE_SYNC_EXPIRED in " +
- (ACTIVE_SYNC_TIMEOUT_MILLIS/1000) + "s");
- }
- Message msg = mSyncHandler.obtainMessage();
- msg.what = SyncHandler.MESSAGE_SYNC_EXPIRED;
- msg.obj = activeSyncContext;
- mSyncHandler.sendMessageDelayed(msg, ACTIVE_SYNC_TIMEOUT_MILLIS);
- }
-
- /**
* Post a delayed message that will monitor the given sync context by periodically checking how
* much network has been used by the uid.
*/
private void postMonitorSyncProgressMessage(ActiveSyncContext activeSyncContext) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "posting MESSAGE_SYNC_MONITOR in " +
+ Slog.v(TAG, "posting MESSAGE_SYNC_MONITOR in " +
(SYNC_MONITOR_WINDOW_LENGTH_MILLIS/1000) + "s");
}
@@ -1046,6 +993,11 @@
mSyncHandler.sendMessageDelayed(monitorMessage, SYNC_MONITOR_WINDOW_LENGTH_MILLIS);
}
+ private void postScheduleSyncMessage(SyncOperation syncOperation) {
+ mSyncHandler.obtainMessage(mSyncHandler.MESSAGE_SCHEDULE_SYNC, syncOperation)
+ .sendToTarget();
+ }
+
/**
* Monitor sync progress by calculating how many bytes it is managing to send to and fro.
*/
@@ -1057,62 +1009,74 @@
* Convenience class for passing parameters for a finished or cancelled sync to the handler
* to be processed.
*/
- class SyncHandlerMessagePayload {
+ private class SyncFinishedOrCancelledMessagePayload {
public final ActiveSyncContext activeSyncContext;
public final SyncResult syncResult;
- SyncHandlerMessagePayload(ActiveSyncContext syncContext,
- SyncResult syncResult) {
+ SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext,
+ SyncResult syncResult) {
this.activeSyncContext = syncContext;
this.syncResult = syncResult;
}
}
- class SyncAlarmIntentReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- mHandleAlarmWakeLock.acquire();
- sendSyncAlarmMessage();
+ private class UpdatePeriodicSyncMessagePayload {
+ public final EndPoint target;
+ public final long pollFrequency;
+ public final long flex;
+ public final Bundle extras;
+
+ UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex,
+ Bundle extras) {
+ this.target = target;
+ this.pollFrequency = pollFrequency;
+ this.flex = flex;
+ this.extras = extras;
}
}
- private void clearBackoffSetting(SyncOperation op) {
- mSyncStorageEngine.setBackoff(op.target,
+ private void clearBackoffSetting(EndPoint target) {
+ Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(target);
+ if (backoff != null && backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE &&
+ backoff.second == SyncStorageEngine.NOT_IN_BACKOFF_MODE) {
+ return;
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "Clearing backoffs for " + target);
+ }
+ mSyncStorageEngine.setBackoff(target,
SyncStorageEngine.NOT_IN_BACKOFF_MODE,
SyncStorageEngine.NOT_IN_BACKOFF_MODE);
- synchronized (mSyncQueue) {
- mSyncQueue.onBackoffChanged(op.target, 0);
- }
+
+ rescheduleSyncs(target);
}
- private void increaseBackoffSetting(SyncOperation op) {
- // TODO: Use this function to align it to an already scheduled sync
- // operation in the specified window
+ private void increaseBackoffSetting(EndPoint target) {
final long now = SystemClock.elapsedRealtime();
final Pair<Long, Long> previousSettings =
- mSyncStorageEngine.getBackoff(op.target);
+ mSyncStorageEngine.getBackoff(target);
long newDelayInMs = -1;
if (previousSettings != null) {
- // don't increase backoff before current backoff is expired. This will happen for op's
+ // Don't increase backoff before current backoff is expired. This will happen for op's
// with ignoreBackoff set.
if (now < previousSettings.first) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Still in backoff, do not increase it. "
- + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds.");
+ Slog.v(TAG, "Still in backoff, do not increase it. "
+ + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds.");
}
return;
}
- // Subsequent delays are the double of the previous delay
+ // Subsequent delays are the double of the previous delay.
newDelayInMs = previousSettings.second * 2;
}
if (newDelayInMs <= 0) {
- // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS
+ // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS.
newDelayInMs = jitterize(INITIAL_SYNC_RETRY_TIME_IN_MS,
(long)(INITIAL_SYNC_RETRY_TIME_IN_MS * 1.1));
}
- // Cap the delay
+ // Cap the delay.
long maxSyncRetryTimeInSeconds = Settings.Global.getLong(mContext.getContentResolver(),
Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS);
@@ -1121,17 +1085,34 @@
}
final long backoff = now + newDelayInMs;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "Backoff until: " + backoff + ", delayTime: " + newDelayInMs);
+ }
+ mSyncStorageEngine.setBackoff(target, backoff, newDelayInMs);
+ rescheduleSyncs(target);
+ }
- mSyncStorageEngine.setBackoff(op.target, backoff, newDelayInMs);
- op.backoff = backoff;
- op.updateEffectiveRunTime();
-
- synchronized (mSyncQueue) {
- mSyncQueue.onBackoffChanged(op.target, backoff);
+ /**
+ * Reschedule all scheduled syncs for this EndPoint. The syncs will be scheduled according
+ * to current backoff and delayUntil values of this EndPoint.
+ */
+ private void rescheduleSyncs(EndPoint target) {
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ int count = 0;
+ for (SyncOperation op: ops) {
+ if (!op.isPeriodic && op.target.matchesSpec(target)) {
+ count++;
+ removeSyncOperationFromCache(op.jobId);
+ getJobScheduler().cancel(op.jobId);
+ postScheduleSyncMessage(op);
+ }
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "Rescheduled " + count + " syncs for " + target);
}
}
- private void setDelayUntilTime(SyncOperation op, long delayUntilSeconds) {
+ private void setDelayUntilTime(EndPoint target, long delayUntilSeconds) {
final long delayUntil = delayUntilSeconds * 1000;
final long absoluteNow = System.currentTimeMillis();
long newDelayUntilTime;
@@ -1140,10 +1121,24 @@
} else {
newDelayUntilTime = 0;
}
- mSyncStorageEngine.setDelayUntilTime(op.target, newDelayUntilTime);
- synchronized (mSyncQueue) {
- mSyncQueue.onDelayUntilTimeChanged(op.target, newDelayUntilTime);
+ mSyncStorageEngine.setDelayUntilTime(target, newDelayUntilTime);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "Delay Until time set to " + newDelayUntilTime + " for " + target);
}
+ rescheduleSyncs(target);
+ }
+
+ private boolean isAdapterDelayed(EndPoint target) {
+ long now = SystemClock.elapsedRealtime();
+ Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(target);
+ if (backoff != null && backoff.first != SyncStorageEngine.NOT_IN_BACKOFF_MODE
+ && backoff.first > now) {
+ return true;
+ }
+ if (mSyncStorageEngine.getDelayUntilTime(target) > now) {
+ return true;
+ }
+ return false;
}
/**
@@ -1157,27 +1152,120 @@
}
/**
- * Create and schedule a SyncOperation.
- *
- * @param syncOperation the SyncOperation to schedule
+ * Schedule a sync operation with JobScheduler.
*/
- public void scheduleSyncOperation(SyncOperation syncOperation) {
- boolean queueChanged;
- synchronized (mSyncQueue) {
- queueChanged = mSyncQueue.add(syncOperation);
+ private void scheduleSyncOperationH(SyncOperation syncOperation) {
+ scheduleSyncOperationH(syncOperation, 0L);
+ }
+
+ private void scheduleSyncOperationH(SyncOperation syncOperation, long minDelay) {
+ final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+ if (syncOperation == null) {
+ Slog.e(TAG, "Can't schedule null sync operation.");
+ return;
+ }
+ if (!syncOperation.ignoreBackoff()) {
+ Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(syncOperation.target);
+ if (backoff == null) {
+ Slog.e(TAG, "Couldn't find backoff values for " + syncOperation.target);
+ backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE,
+ SyncStorageEngine.NOT_IN_BACKOFF_MODE);
+ }
+ long now = SystemClock.elapsedRealtime();
+ long backoffDelay = backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE ? 0
+ : backoff.first - now;
+ long delayUntil = mSyncStorageEngine.getDelayUntilTime(syncOperation.target);
+ long delayUntilDelay = delayUntil > now ? delayUntil - now : 0;
+ if (isLoggable) {
+ Slog.v(TAG, "backoff delay:" + backoffDelay
+ + " delayUntil delay:" + delayUntilDelay);
+ }
+ minDelay = Math.max(minDelay, Math.max(backoffDelay, delayUntilDelay));
}
- if (queueChanged) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "scheduleSyncOperation: enqueued " + syncOperation);
+ if (minDelay < 0) {
+ minDelay = 0;
+ }
+
+ // Check if duplicate syncs are pending. If found, keep one with least expected run time.
+ if (!syncOperation.isReasonPeriodic()) {
+ int duplicatesCount = 0;
+ long now = SystemClock.elapsedRealtime();
+ syncOperation.expectedRuntime = now + minDelay;
+ List<SyncOperation> pending = getAllPendingSyncsFromCache();
+ SyncOperation opWithLeastExpectedRuntime = syncOperation;
+ for (SyncOperation op : pending) {
+ if (op.isPeriodic) {
+ continue;
+ }
+ if (op.key.equals(syncOperation.key)) {
+ if (opWithLeastExpectedRuntime.expectedRuntime > op.expectedRuntime) {
+ opWithLeastExpectedRuntime = op;
+ }
+ duplicatesCount++;
+ }
}
- sendCheckAlarmsMessage();
- } else {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "scheduleSyncOperation: dropping duplicate sync operation "
- + syncOperation);
+ if (duplicatesCount > 1) {
+ Slog.e(TAG, "FATAL ERROR! File a bug if you see this.");
+ }
+ for (SyncOperation op : pending) {
+ if (op.isPeriodic) {
+ continue;
+ }
+ if (op.key.equals(syncOperation.key)) {
+ if (op != opWithLeastExpectedRuntime) {
+ if (isLoggable) {
+ Slog.v(TAG, "Cancelling duplicate sync " + op);
+ }
+ removeSyncOperationFromCache(op.jobId);
+ getJobScheduler().cancel(op.jobId);
+ }
+ }
+ }
+ if (opWithLeastExpectedRuntime != syncOperation) {
+ // Don't schedule because a duplicate sync with earlier expected runtime exists.
+ if (isLoggable) {
+ Slog.v(TAG, "Not scheduling because a duplicate exists.");
+ }
+ return;
}
}
+
+ syncOperation.jobId = getUnusedJobId();
+ addSyncOperationToCache(syncOperation);
+
+ if (isLoggable) {
+ Slog.v(TAG, "scheduling sync operation " + syncOperation.target.toString());
+ }
+
+ // This is done to give preference to syncs that are not pushed back.
+ int priority = syncOperation.findPriority();
+
+ final int networkType = syncOperation.isNotAllowedOnMetered() ?
+ JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;
+
+ JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
+ new ComponentName(mContext, SyncJobService.class))
+ .setExtras(syncOperation.toJobInfoExtras())
+ .setRequiredNetworkType(networkType)
+ .setPersisted(true)
+ .setPriority(priority);
+
+ if (syncOperation.isPeriodic) {
+ b.setPeriodic(syncOperation.periodMillis, syncOperation.flexMillis);
+ } else {
+ if (minDelay > 0) {
+ b.setMinimumLatency(minDelay);
+ }
+ getSyncStorageEngine().markPending(syncOperation.target, true);
+ }
+
+ if (syncOperation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING)) {
+ b.setRequiresCharging(true);
+ }
+
+ getJobScheduler().scheduleAsPackage(b.build(), syncOperation.owningPackage,
+ syncOperation.target.userId);
}
/**
@@ -1186,8 +1274,13 @@
* have null account/provider info to specify all accounts/providers.
*/
public void clearScheduledSyncOperations(SyncStorageEngine.EndPoint info) {
- synchronized (mSyncQueue) {
- mSyncQueue.remove(info, null /* all operations */);
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ for (SyncOperation op: ops) {
+ if (!op.isPeriodic && op.target.matchesSpec(info)) {
+ removeSyncOperationFromCache(op.jobId);
+ getJobScheduler().cancel(op.jobId);
+ getSyncStorageEngine().markPending(op.target, false);
+ }
}
mSyncStorageEngine.setBackoff(info,
SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
@@ -1199,9 +1292,15 @@
* @param extras extras bundle to uniquely identify sync.
*/
public void cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras) {
- synchronized (mSyncQueue) {
- mSyncQueue.remove(info, extras);
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ for (SyncOperation op: ops) {
+ if (!op.isPeriodic && op.target.matchesSpec(info)
+ && syncExtrasEquals(extras, op.extras, false)) {
+ removeSyncOperationFromCache(op.jobId);
+ getJobScheduler().cancel(op.jobId);
+ }
}
+ setAuthorityPendingState(info);
// Reset the back-off if there are no more syncs pending.
if (!mSyncStorageEngine.isSyncPending(info)) {
mSyncStorageEngine.setBackoff(info,
@@ -1209,14 +1308,12 @@
}
}
- void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) {
- boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
+ private void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) {
+ final boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
if (isLoggable) {
Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation);
}
- operation = new SyncOperation(operation, 0L /* newRunTimeFromNow */);
-
// The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given
// request. Retries of the request will always honor the backoff, so clear the
// flag in case we retry this request.
@@ -1237,7 +1334,7 @@
Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
+ "encountered an error: " + operation);
}
- scheduleSyncOperation(operation);
+ scheduleSyncOperationH(operation, 0 /* immediately */);
} 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.
@@ -1251,24 +1348,20 @@
Log.d(TAG, "retrying sync operation because even though it had an error "
+ "it achieved some success");
}
- scheduleSyncOperation(operation);
+ scheduleSyncOperationH(operation, 0 /* immediately */);
} else if (syncResult.syncAlreadyInProgress) {
if (isLoggable) {
Log.d(TAG, "retrying sync operation that failed because there was already a "
+ "sync in progress: " + operation);
}
- scheduleSyncOperation(
- new SyncOperation(
- operation,
- DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000 /* newRunTimeFromNow */)
- );
+ scheduleSyncOperationH(operation, DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000);
} else if (syncResult.hasSoftError()) {
// If this was a two-way sync then retry soft errors with an exponential backoff.
if (isLoggable) {
Log.d(TAG, "retrying sync operation because it encountered a soft error: "
+ operation);
}
- scheduleSyncOperation(operation);
+ scheduleSyncOperationH(operation);
} else {
// Otherwise do not reschedule.
Log.d(TAG, "not retrying sync operation because the error is a hard error: "
@@ -1277,85 +1370,46 @@
}
private void onUserUnlocked(int userId) {
- // Make sure that accounts we're about to use are valid
+ // Make sure that accounts we're about to use are valid.
AccountManagerService.getSingleton().validateAccounts(userId);
mSyncAdapters.invalidateCache(userId);
- updateRunningAccounts();
+ EndPoint target = new EndPoint(null, null, userId);
+ updateRunningAccounts(target);
- synchronized (mSyncQueue) {
- mSyncQueue.addPendingOperations(userId);
- }
-
- // Schedule sync for any accounts under started user
+ // Schedule sync for any accounts under started user.
final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId,
mContext.getOpPackageName());
for (Account account : accounts) {
scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
- 0 /* no delay */, 0 /* No flex */,
+ 0 /* no delay */, 0 /* No flexMillis */,
true /* onlyThoseWithUnknownSyncableState */);
}
-
- sendCheckAlarmsMessage();
}
private void onUserStopping(int userId) {
- updateRunningAccounts();
+ updateRunningAccounts(null /* Don't sync any target */);
cancelActiveSync(
new SyncStorageEngine.EndPoint(
null /* any account */,
null /* any authority */,
userId),
- null /* any sync. */
- );
+ null /* any sync. */
+ );
}
private void onUserRemoved(int userId) {
- updateRunningAccounts();
+ updateRunningAccounts(null /* Don't sync any target */);
// Clean up the storage engine database
mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
- synchronized (mSyncQueue) {
- mSyncQueue.removeUserLocked(userId);
- }
- }
-
- /**
- * Clear backoff on operations in the sync queue that match the packageName and userId.
- * @param packageName The package that just became active. Can be null to indicate that all
- * packages are now considered active due to being plugged in.
- * @param userId The user for which the package has become active. Can be USER_ALL if
- * the device just plugged in.
- */
- void onAppNotIdle(@Nullable String packageName, int userId) {
- synchronized (mSyncQueue) {
- // For all sync operations in sync queue, if marked as idle, compare with package name
- // and unmark. And clear backoff for the operation.
- final Iterator<SyncOperation> operationIterator =
- mSyncQueue.getOperations().iterator();
- boolean changed = false;
- while (operationIterator.hasNext()) {
- final SyncOperation op = operationIterator.next();
- if (op.appIdle
- && (packageName == null || getPackageName(op.target).equals(packageName))
- && (userId == UserHandle.USER_ALL || op.target.userId == userId)) {
- op.appIdle = false;
- clearBackoffSetting(op);
- changed = true;
- }
- }
- if (changed) {
- sendCheckAlarmsMessage();
- }
- }
- }
-
- void cancelSyncsForUid(int uid) {
- synchronized (mSyncQueue) {
- if (mSyncQueue.removeUidIfNeededLocked(uid)) {
- sendCheckAlarmsMessage();
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ for (SyncOperation op: ops) {
+ if (op.target.userId == userId) {
+ removeSyncOperationFromCache(op.jobId);
+ getJobScheduler().cancel(op.jobId);
}
}
}
@@ -1368,7 +1422,6 @@
final SyncOperation mSyncOperation;
final long mHistoryRowId;
ISyncAdapter mSyncAdapter;
- ISyncServiceAdapter mSyncServiceAdapter;
final long mStartTime;
long mTimeoutStartTime;
boolean mBound;
@@ -1397,13 +1450,12 @@
* for this sync. This is used to attribute the wakelock hold to that application.
*/
public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
- int syncAdapterUid) {
+ int syncAdapterUid) {
super();
mSyncAdapterUid = syncAdapterUid;
mSyncOperation = syncOperation;
mHistoryRowId = historyRowId;
mSyncAdapter = null;
- mSyncServiceAdapter = null;
mStartTime = SystemClock.elapsedRealtime();
mTimeoutStartTime = mStartTime;
mSyncWakeLock = mSyncHandler.getSyncWakeLock(mSyncOperation);
@@ -1412,14 +1464,14 @@
}
public void sendHeartbeat() {
- // heartbeats are no longer used
+ // Heartbeats are no longer used.
}
public void onFinished(SyncResult result) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "onFinished: " + this);
- // include "this" in the message so that the handler can ignore it if this
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "onFinished: " + this);
+ // Include "this" in the message so that the handler can ignore it if this
// ActiveSyncContext is no longer the mActiveSyncContext at message handling
- // time
+ // time.
sendSyncFinishedOrCanceledMessage(this, result);
}
@@ -1459,7 +1511,7 @@
mBound = true;
final boolean bindResult = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_ALLOW_OOM_MANAGEMENT,
+ | Context.BIND_ALLOW_OOM_MANAGEMENT,
new UserHandle(mSyncOperation.target.userId));
if (!bindResult) {
mBound = false;
@@ -1507,6 +1559,8 @@
protected void dump(FileDescriptor fd, PrintWriter pw) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ dumpPendingSyncs(pw);
+ dumpPeriodicSyncs(pw);
dumpSyncState(ipw);
dumpSyncHistory(ipw);
dumpSyncAdapters(ipw);
@@ -1518,6 +1572,34 @@
return tobj.format("%Y-%m-%d %H:%M:%S");
}
+ protected void dumpPendingSyncs(PrintWriter pw) {
+ pw.println("Pending Syncs:");
+ List<SyncOperation> pendingSyncs = getAllPendingSyncsFromCache();
+ int count = 0;
+ for (SyncOperation op: pendingSyncs) {
+ if (!op.isPeriodic) {
+ pw.println(op.dump(null, false));
+ count++;
+ }
+ }
+ pw.println("Total: " + count);
+ pw.println();
+ }
+
+ protected void dumpPeriodicSyncs(PrintWriter pw) {
+ pw.println("Periodic Syncs:");
+ List<SyncOperation> pendingSyncs = getAllPendingSyncsFromCache();
+ int count = 0;
+ for (SyncOperation op: pendingSyncs) {
+ if (op.isPeriodic) {
+ pw.println(op.dump(null, false));
+ count++;
+ }
+ }
+ pw.println("Total: " + count);
+ pw.println();
+ }
+
protected void dumpSyncState(PrintWriter pw) {
pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
pw.print("auto sync: ");
@@ -1544,24 +1626,15 @@
final long now = SystemClock.elapsedRealtime();
pw.print("now: "); pw.print(now);
pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
- pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis / 1000));
pw.println(" (HH:MM:SS)");
pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now / 1000));
- pw.println(" (HH:MM:SS)");
+ pw.println(" (HH:MM:SS)");
pw.print("time spent syncing: ");
- pw.print(DateUtils.formatElapsedTime(
- mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000));
- pw.print(" (HH:MM:SS), sync ");
- pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
- pw.println("in progress");
- if (mSyncHandler.mAlarmScheduleTime != null) {
- pw.print("next alarm time: "); pw.print(mSyncHandler.mAlarmScheduleTime);
- pw.print(" (");
- pw.print(DateUtils.formatElapsedTime((mSyncHandler.mAlarmScheduleTime-now)/1000));
- pw.println(" (HH:MM:SS) from now)");
- } else {
- pw.println("no alarm is scheduled (there had better not be any pending syncs)");
- }
+ pw.print(DateUtils.formatElapsedTime(
+ mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000));
+ pw.print(" (HH:MM:SS), sync ");
+ pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
+ pw.println("in progress");
pw.println();
pw.println("Active Syncs: " + mActiveSyncContexts.size());
@@ -1575,17 +1648,7 @@
pw.println();
}
- final StringBuilder sb = new StringBuilder();
- synchronized (mSyncQueue) {
- mSyncQueue.dump(sb);
- // Dump Pending Operations.
- getSyncStorageEngine().dumpPendingOperations(sb);
- }
-
- pw.println();
- pw.print(sb.toString());
-
- // join the installed sync adapter with the accounts list and emit for everything
+ // Join the installed sync adapter with the accounts list and emit for everything.
pw.println();
pw.println("Sync Status");
for (AccountAndUser account : accounts) {
@@ -1593,7 +1656,7 @@
account.account.name, account.userId, account.account.type);
pw.println("=======================================================================");
- final PrintTable table = new PrintTable(13);
+ final PrintTable table = new PrintTable(12);
table.set(0, 0,
"Authority", // 0
"Syncable", // 1
@@ -1606,8 +1669,7 @@
"User", // 8
"Tot", // 9
"Time", // 10
- "Last Sync", // 11
- "Periodic" // 12
+ "Last Sync" // 11
);
final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted =
@@ -1615,18 +1677,18 @@
sorted.addAll(mSyncAdapters.getAllServices(account.userId));
Collections.sort(sorted,
new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
- @Override
- public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
- RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
- return lhs.type.authority.compareTo(rhs.type.authority);
- }
- });
+ @Override
+ public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
+ RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
+ return lhs.type.authority.compareTo(rhs.type.authority);
+ }
+ });
for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) {
if (!syncAdapterType.type.accountType.equals(account.account.type)) {
continue;
}
int row = table.getNumRows();
- Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus =
+ Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus =
mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus(
new SyncStorageEngine.EndPoint(
account.account,
@@ -1648,20 +1710,6 @@
status.numSyncs,
DateUtils.formatElapsedTime(status.totalElapsedTime / 1000));
-
- for (int i = 0; i < settings.periodicSyncs.size(); i++) {
- final PeriodicSync sync = settings.periodicSyncs.get(i);
- final String period =
- String.format("[p:%d s, f: %d s]", sync.period, sync.flexTime);
- final String extras =
- sync.extras.size() > 0 ?
- sync.extras.toString() : "Bundle[]";
- final String next = "Next sync: " + formatTime(status.getPeriodicSyncTime(i)
- + sync.period * 1000);
- table.set(row + i * 2, 12, period + " " + extras);
- table.set(row + i * 2 + 1, 12, next);
- }
-
int row1 = row;
if (settings.delayUntil > now) {
table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000);
@@ -1688,37 +1736,6 @@
}
}
- private String getLastFailureMessage(int code) {
- switch (code) {
- case ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS:
- return "sync already in progress";
-
- case ContentResolver.SYNC_ERROR_AUTHENTICATION:
- return "authentication error";
-
- case ContentResolver.SYNC_ERROR_IO:
- return "I/O error";
-
- case ContentResolver.SYNC_ERROR_PARSE:
- return "parse error";
-
- case ContentResolver.SYNC_ERROR_CONFLICT:
- return "conflict error";
-
- case ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS:
- return "too many deletions error";
-
- case ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES:
- return "too many retries error";
-
- case ContentResolver.SYNC_ERROR_INTERNAL:
- return "internal error";
-
- default:
- return "unknown";
- }
- }
-
private void dumpTimeSec(PrintWriter pw, long time) {
pw.print(time/1000); pw.print('.'); pw.print((time/100)%10);
pw.print('s');
@@ -1760,20 +1777,10 @@
final String authorityName;
final String accountKey;
if (authorityInfo != null) {
- if (authorityInfo.target.target_provider) {
- authorityName = authorityInfo.target.provider;
- accountKey = authorityInfo.target.account.name + "/"
- + authorityInfo.target.account.type
- + " u" + authorityInfo.target.userId;
- } else if (authorityInfo.target.target_service) {
- authorityName = authorityInfo.target.service.getPackageName() + "/"
- + authorityInfo.target.service.getClassName()
- + " u" + authorityInfo.target.userId;
- accountKey = "no account";
- } else {
- authorityName = "Unknown";
- accountKey = "Unknown";
- }
+ authorityName = authorityInfo.target.provider;
+ accountKey = authorityInfo.target.account.name + "/"
+ + authorityInfo.target.account.type
+ + " u" + authorityInfo.target.userId;
} else {
authorityName = "Unknown";
accountKey = "Unknown";
@@ -1812,7 +1819,7 @@
if (totalElapsedTime > 0) {
pw.println();
pw.printf("Detailed Statistics (Recent history): "
- + "%d (# of times) %ds (sync time)\n",
+ + "%d (# of times) %ds (sync time)\n",
totalTimes, totalElapsedTime / 1000);
final List<AuthoritySyncStats> sortedAuthorities =
@@ -1899,20 +1906,10 @@
final String authorityName;
final String accountKey;
if (authorityInfo != null) {
- if (authorityInfo.target.target_provider) {
- authorityName = authorityInfo.target.provider;
- accountKey = authorityInfo.target.account.name + "/"
- + authorityInfo.target.account.type
- + " u" + authorityInfo.target.userId;
- } else if (authorityInfo.target.target_service) {
- authorityName = authorityInfo.target.service.getPackageName() + "/"
- + authorityInfo.target.service.getClassName()
- + " u" + authorityInfo.target.userId;
- accountKey = "none";
- } else {
- authorityName = "Unknown";
- accountKey = "Unknown";
- }
+ authorityName = authorityInfo.target.provider;
+ accountKey = authorityInfo.target.account.name + "/"
+ + authorityInfo.target.account.type
+ + " u" + authorityInfo.target.userId;
} else {
authorityName = "Unknown";
accountKey = "Unknown";
@@ -1976,20 +1973,10 @@
final String authorityName;
final String accountKey;
if (authorityInfo != null) {
- if (authorityInfo.target.target_provider) {
- authorityName = authorityInfo.target.provider;
- accountKey = authorityInfo.target.account.name + "/"
- + authorityInfo.target.account.type
- + " u" + authorityInfo.target.userId;
- } else if (authorityInfo.target.target_service) {
- authorityName = authorityInfo.target.service.getPackageName() + "/"
- + authorityInfo.target.service.getClassName()
- + " u" + authorityInfo.target.userId;
- accountKey = "none";
- } else {
- authorityName = "Unknown";
- accountKey = "Unknown";
- }
+ authorityName = authorityInfo.target.provider;
+ accountKey = authorityInfo.target.account.name + "/"
+ + authorityInfo.target.account.type
+ + " u" + authorityInfo.target.userId;
} else {
authorityName = "Unknown";
accountKey = "Unknown";
@@ -2146,13 +2133,18 @@
* HandlerThread.
*/
class SyncHandler extends Handler {
- // Messages that can be sent on mHandler
+ // Messages that can be sent on mHandler.
private static final int MESSAGE_SYNC_FINISHED = 1;
- private static final int MESSAGE_SYNC_ALARM = 2;
- private static final int MESSAGE_CHECK_ALARMS = 3;
+ private static final int MESSAGE_RELEASE_MESSAGES_FROM_QUEUE = 2;
private static final int MESSAGE_SERVICE_CONNECTED = 4;
private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
private static final int MESSAGE_CANCEL = 6;
+ static final int MESSAGE_JOBSERVICE_OBJECT = 7;
+ static final int MESSAGE_START_SYNC = 10;
+ static final int MESSAGE_STOP_SYNC = 11;
+ static final int MESSAGE_SCHEDULE_SYNC = 12;
+ static final int MESSAGE_UPDATE_PERIODIC_SYNC = 13;
+ static final int MESSAGE_REMOVE_PERIODIC_SYNC = 14;
/**
* Posted delayed in order to expire syncs that are long-running.
* obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
@@ -2165,8 +2157,6 @@
private static final int MESSAGE_MONITOR_SYNC = 8;
private static final int MESSAGE_ACCOUNTS_UPDATED = 9;
- public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
- private Long mAlarmScheduleTime = null;
public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
@@ -2174,32 +2164,230 @@
void onBootCompleted() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Boot completed, clearing boot queue.");
+ Slog.v(TAG, "Boot completed.");
}
- doDatabaseCleanup();
- synchronized(this) {
- // Dispatch any stashed messages.
- maybeEmptyUnreadyQueueLocked();
- }
+ checkIfDeviceReady();
}
void onDeviceProvisioned() {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "mProvisioned=" + mProvisioned);
}
- synchronized (this) {
- maybeEmptyUnreadyQueueLocked();
+ checkIfDeviceReady();
+ }
+
+ void checkIfDeviceReady() {
+ if (mProvisioned && mBootCompleted) {
+ synchronized(this) {
+ // Dispatch any stashed messages.
+ obtainMessage(MESSAGE_RELEASE_MESSAGES_FROM_QUEUE).sendToTarget();
+ }
}
}
- private void maybeEmptyUnreadyQueueLocked() {
- if (mProvisioned && mBootCompleted) {
- // Dispatch any stashed messages.
- for (int i=0; i<mUnreadyQueue.size(); i++) {
- sendMessageDelayed(mUnreadyQueue.get(i),
- Math.max(PER_SYNC_BOOT_DELAY_MILLIS * i, MAX_SYNC_BOOT_DELAY_MILLIS));
+ /**
+ * Stash any messages that come to the handler before boot is complete or before the device
+ * is properly provisioned (i.e. out of set-up wizard).
+ * {@link #onBootCompleted()} and {@link SyncHandler#onDeviceProvisioned} both
+ * need to come in before we start syncing.
+ * @param msg Message to dispatch at a later point.
+ * @return true if a message was enqueued, false otherwise. This is to avoid losing the
+ * message if we manage to acquire the lock but by the time we do boot has completed.
+ */
+ private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
+ synchronized (this) {
+ if (!mBootCompleted || !mProvisioned) {
+ if (msg.what == MESSAGE_START_SYNC) {
+ deferSyncH((SyncOperation) msg.obj, 60*1000 /* 1 minute */);
+ }
+ // Need to copy the message bc looper will recycle it.
+ Message m = Message.obtain(msg);
+ mUnreadyQueue.add(m);
+ return true;
+ } else {
+ return false;
}
- mUnreadyQueue = null;
+ }
+ }
+
+ public SyncHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void handleMessage(Message msg) {
+ try {
+ mSyncManagerWakeLock.acquire();
+ // We only want to enqueue sync related messages until device is ready.
+ // Other messages are handled without enqueuing.
+ if (msg.what == MESSAGE_JOBSERVICE_OBJECT) {
+ Slog.i(TAG, "Got SyncJobService instance.");
+ mSyncJobService = (SyncJobService) msg.obj;
+ } else if (msg.what == SyncHandler.MESSAGE_ACCOUNTS_UPDATED) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
+ }
+ EndPoint targets = (EndPoint) msg.obj;
+ updateRunningAccountsH(targets);
+ } else if (msg.what == MESSAGE_RELEASE_MESSAGES_FROM_QUEUE) {
+ if (mUnreadyQueue != null) {
+ for (Message m : mUnreadyQueue) {
+ handleSyncMessage(m);
+ }
+ mUnreadyQueue = null;
+ }
+ } else if (tryEnqueueMessageUntilReadyToRun(msg)) {
+ // No work to be done.
+ } else {
+ handleSyncMessage(msg);
+ }
+ } finally {
+ mSyncManagerWakeLock.release();
+ }
+ }
+
+ private void handleSyncMessage(Message msg) {
+ final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+
+ try {
+ mDataConnectionIsConnected = readDataConnectionState();
+ switch (msg.what) {
+ case MESSAGE_SCHEDULE_SYNC:
+ SyncOperation op = (SyncOperation) msg.obj;
+ scheduleSyncOperationH(op);
+ break;
+
+ case MESSAGE_START_SYNC:
+ op = (SyncOperation) msg.obj;
+ startSyncH(op);
+ break;
+
+ 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);
+ }
+ ActiveSyncContext asc = findActiveSyncContextH(op.jobId);
+ if (asc != null) {
+ runSyncFinishedOrCanceledH(null /* no result */, asc);
+ }
+ break;
+
+ case MESSAGE_UPDATE_PERIODIC_SYNC:
+ UpdatePeriodicSyncMessagePayload data =
+ (UpdatePeriodicSyncMessagePayload) msg.obj;
+ updateOrAddPeriodicSyncH(data.target, data.pollFrequency,
+ data.flex, data.extras);
+ break;
+ case MESSAGE_REMOVE_PERIODIC_SYNC:
+ removePeriodicSyncH((EndPoint)msg.obj, msg.getData());
+ break;
+
+ case SyncHandler.MESSAGE_CANCEL:
+ SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
+ Bundle extras = msg.peekData();
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: "
+ + endpoint + " bundle: " + extras);
+ }
+ cancelActiveSyncH(endpoint, extras);
+ break;
+
+ case SyncHandler.MESSAGE_SYNC_FINISHED:
+ SyncFinishedOrCancelledMessagePayload payload =
+ (SyncFinishedOrCancelledMessagePayload) msg.obj;
+ if (!isSyncStillActiveH(payload.activeSyncContext)) {
+ Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
+ + "sync is no longer active: "
+ + payload.activeSyncContext);
+ break;
+ }
+ if (isLoggable) {
+ Slog.v(TAG, "syncFinished" + payload.activeSyncContext.mSyncOperation);
+ }
+ mSyncJobService.callJobFinished(
+ payload.activeSyncContext.mSyncOperation.jobId, false);
+ runSyncFinishedOrCanceledH(payload.syncResult,
+ payload.activeSyncContext);
+ break;
+
+ case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
+ ServiceConnectionData msgData = (ServiceConnectionData) msg.obj;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
+ + msgData.activeSyncContext);
+ }
+ // Check that this isn't an old message.
+ if (isSyncStillActiveH(msgData.activeSyncContext)) {
+ runBoundToAdapterH(
+ msgData.activeSyncContext,
+ msgData.adapter);
+ }
+ break;
+ }
+
+ case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
+ final ActiveSyncContext currentSyncContext =
+ ((ServiceConnectionData) msg.obj).activeSyncContext;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
+ + currentSyncContext);
+ }
+ // Check that this isn't an old message.
+ if (isSyncStillActiveH(currentSyncContext)) {
+ // cancel the sync if we have a syncadapter, which means one is
+ // outstanding
+ try {
+ if (currentSyncContext.mSyncAdapter != null) {
+ currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
+ }
+ } catch (RemoteException e) {
+ // We don't need to retry this in this case.
+ }
+
+ // Pretend that the sync failed with an IOException,
+ // which is a soft error.
+ SyncResult syncResult = new SyncResult();
+ syncResult.stats.numIoExceptions++;
+ mSyncJobService.callJobFinished(
+ currentSyncContext.mSyncOperation.jobId, false);
+ runSyncFinishedOrCanceledH(syncResult, currentSyncContext);
+ }
+ break;
+ }
+
+ case SyncHandler.MESSAGE_MONITOR_SYNC:
+ ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj;
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " +
+ monitoredSyncContext.mSyncOperation.target);
+ }
+
+ if (isSyncNotUsingNetworkH(monitoredSyncContext)) {
+ Log.w(TAG, String.format(
+ "Detected sync making no progress for %s. cancelling.",
+ monitoredSyncContext));
+ mSyncJobService.callJobFinished(
+ monitoredSyncContext.mSyncOperation.jobId, false);
+ runSyncFinishedOrCanceledH(
+ null /* cancel => no result */, monitoredSyncContext);
+ } else {
+ // Repost message to check again.
+ postMonitorSyncProgressMessage(monitoredSyncContext);
+ }
+ break;
+
+ }
+ } finally {
+ mSyncTimeTracker.update();
}
}
@@ -2216,664 +2404,117 @@
}
/**
- * Stash any messages that come to the handler before boot is complete or before the device
- * is properly provisioned (i.e. out of set-up wizard).
- * {@link #onBootCompleted()} and {@link SyncHandler#onDeviceProvisioned} both
- * need to come in before we start syncing.
- * @param msg Message to dispatch at a later point.
- * @return true if a message was enqueued, false otherwise. This is to avoid losing the
- * message if we manage to acquire the lock but by the time we do boot has completed.
+ * Defer the specified SyncOperation by rescheduling it on the JobScheduler with some
+ * delay.
*/
- private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
- synchronized (this) {
- if (!mBootCompleted || !mProvisioned) {
- // Need to copy the message bc looper will recycle it.
- mUnreadyQueue.add(Message.obtain(msg));
- return true;
- } else {
- return false;
- }
+ private void deferSyncH(SyncOperation op, long delay) {
+ mSyncJobService.callJobFinished(op.jobId, false);
+ if (op.isPeriodic) {
+ scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
+ } else {
+ removeSyncOperationFromCache(op.jobId);
+ scheduleSyncOperationH(op, delay);
}
}
/**
- * Used to keep track of whether a sync notification is active and who it is for.
+ * Cancel an active sync and reschedule it on the JobScheduler with some delay.
*/
- class SyncNotificationInfo {
- // true iff the notification manager has been asked to send the notification
- public boolean isActive = false;
+ private void deferActiveSyncH(ActiveSyncContext asc) {
+ SyncOperation op = asc.mSyncOperation;
- // Set when we transition from not running a sync to running a sync, and cleared on
- // the opposite transition.
- public Long startTime = null;
-
- public void toString(StringBuilder sb) {
- sb.append("isActive ").append(isActive).append(", startTime ").append(startTime);
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- toString(sb);
- return sb.toString();
- }
+ mSyncHandler.obtainMessage(MESSAGE_STOP_SYNC, 0 /* no reschedule */,
+ 0 /* no backoff */, op).sendToTarget();
+ deferSyncH(op, SYNC_DELAY_ON_CONFLICT);
}
- public SyncHandler(Looper looper) {
- super(looper);
- }
+ private void startSyncH(SyncOperation op) {
+ final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+ if (isLoggable) Slog.v(TAG, op.toString());
- public void handleMessage(Message msg) {
- if (tryEnqueueMessageUntilReadyToRun(msg)) {
+ if (mStorageIsLow) {
+ deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE);
return;
}
- long earliestFuturePollTime = Long.MAX_VALUE;
- long nextPendingSyncTime = Long.MAX_VALUE;
- // Setting the value here instead of a method because we want the dumpsys logs
- // to have the most recent value used.
- try {
- mDataConnectionIsConnected = readDataConnectionState();
- mSyncManagerWakeLock.acquire();
- // Always do this first so that we be sure that any periodic syncs that
- // are ready to run have been converted into pending syncs. This allows the
- // logic that considers the next steps to take based on the set of pending syncs
- // to also take into account the periodic syncs.
- earliestFuturePollTime = scheduleReadyPeriodicSyncs();
- switch (msg.what) {
- case SyncHandler.MESSAGE_ACCOUNTS_UPDATED:
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
- }
- updateRunningAccountsH();
- break;
-
- case SyncHandler.MESSAGE_CANCEL:
- SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
- Bundle extras = msg.peekData();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: "
- + endpoint + " bundle: " + extras);
- }
- cancelActiveSyncH(endpoint, extras);
- nextPendingSyncTime = maybeStartNextSyncH();
- break;
-
- case SyncHandler.MESSAGE_SYNC_FINISHED:
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED");
- }
- SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload) msg.obj;
- if (!isSyncStillActiveH(payload.activeSyncContext)) {
- Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
- + "sync is no longer active: "
- + payload.activeSyncContext);
- break;
- }
- runSyncFinishedOrCanceledH(payload.syncResult,
- payload.activeSyncContext);
-
- // since a sync just finished check if it is time to start a new sync
- nextPendingSyncTime = maybeStartNextSyncH();
- break;
-
- case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
- ServiceConnectionData msgData = (ServiceConnectionData) msg.obj;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
- + msgData.activeSyncContext);
- }
- // check that this isn't an old message
- if (isSyncStillActiveH(msgData.activeSyncContext)) {
- runBoundToAdapter(
- msgData.activeSyncContext,
- msgData.adapter);
- }
- break;
+ if (op.isPeriodic) {
+ // Don't allow this periodic to run if a previous instance failed and is currently
+ // scheduled according to some backoff criteria.
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ for (SyncOperation syncOperation: ops) {
+ if (syncOperation.sourcePeriodicId == op.jobId) {
+ mSyncJobService.callJobFinished(op.jobId, false);
+ return;
}
-
- case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
- final ActiveSyncContext currentSyncContext =
- ((ServiceConnectionData) msg.obj).activeSyncContext;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
- + currentSyncContext);
- }
- // check that this isn't an old message
- if (isSyncStillActiveH(currentSyncContext)) {
- // cancel the sync if we have a syncadapter, which means one is
- // outstanding
- try {
- if (currentSyncContext.mSyncAdapter != null) {
- currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
- } else if (currentSyncContext.mSyncServiceAdapter != null) {
- currentSyncContext.mSyncServiceAdapter
- .cancelSync(currentSyncContext);
- }
- } catch (RemoteException e) {
- // We don't need to retry this in this case.
- }
-
- // pretend that the sync failed with an IOException,
- // which is a soft error
- SyncResult syncResult = new SyncResult();
- syncResult.stats.numIoExceptions++;
- runSyncFinishedOrCanceledH(syncResult, currentSyncContext);
-
- // since a sync just finished check if it is time to start a new sync
- nextPendingSyncTime = maybeStartNextSyncH();
- }
- break;
+ }
+ // Don't allow this periodic to run if a previous instance failed and is currently
+ // executing according to some backoff criteria.
+ for (ActiveSyncContext asc: mActiveSyncContexts) {
+ if (asc.mSyncOperation.sourcePeriodicId == op.jobId) {
+ mSyncJobService.callJobFinished(op.jobId, false);
+ return;
}
+ }
+ // Check for adapter delays.
+ if (isAdapterDelayed(op.target)) {
+ deferSyncH(op, 0 /* No minimum delay */);
+ }
+ } else {
+ // Remove SyncOperation entry from mScheduledSyncs cache for non periodic jobs.
+ removeSyncOperationFromCache(op.jobId);
+ }
- case SyncHandler.MESSAGE_SYNC_ALARM: {
- boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+ // Check for conflicting syncs.
+ for (ActiveSyncContext asc: mActiveSyncContexts) {
+ if (asc.mSyncOperation.isConflict(op)) {
+ // If the provided SyncOperation conflicts with a running one, the lower
+ // priority sync is pre-empted.
+ if (asc.mSyncOperation.findPriority() >= op.findPriority()) {
if (isLoggable) {
- Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_ALARM");
+ Slog.v(TAG, "Rescheduling sync due to conflict " + op.toString());
}
- mAlarmScheduleTime = null;
- try {
- nextPendingSyncTime = maybeStartNextSyncH();
- } finally {
- mHandleAlarmWakeLock.release();
- }
- break;
- }
-
- case SyncHandler.MESSAGE_CHECK_ALARMS:
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS");
- }
- nextPendingSyncTime = maybeStartNextSyncH();
- break;
- case SyncHandler.MESSAGE_SYNC_EXPIRED:
- ActiveSyncContext expiredContext = (ActiveSyncContext) msg.obj;
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_EXPIRED:" +
- " cancelling " + expiredContext);
- }
- runSyncFinishedOrCanceledH(
- null /* cancel => no result */,
- expiredContext);
- nextPendingSyncTime = maybeStartNextSyncH();
- break;
- case SyncHandler.MESSAGE_MONITOR_SYNC:
- ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj;
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " +
- monitoredSyncContext.mSyncOperation.target);
- }
-
- if (isSyncNotUsingNetworkH(monitoredSyncContext)) {
- Log.w(TAG, String.format(
- "Detected sync making no progress for %s. cancelling.",
- monitoredSyncContext));
- runSyncFinishedOrCanceledH(
- null /* cancel => no result */, monitoredSyncContext);
- } else {
- // Repost message to check again.
- postMonitorSyncProgressMessage(monitoredSyncContext);
- }
- break;
-
- }
- } finally {
- manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime);
- mSyncTimeTracker.update();
- mSyncManagerWakeLock.release();
- }
- }
-
- private boolean isDispatchable(SyncStorageEngine.EndPoint target) {
- final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
- if (target.target_provider) {
- // skip the sync if the account of this operation no longer exists
- AccountAndUser[] accounts = mRunningAccounts;
- if (!containsAccountAndUser(
- accounts, target.account, target.userId)) {
- return false;
- }
- if (!mSyncStorageEngine.getMasterSyncAutomatically(target.userId)
- || !mSyncStorageEngine.getSyncAutomatically(
- target.account,
- target.userId,
- target.provider)) {
- if (isLoggable) {
- Log.v(TAG, " Not scheduling periodic operation: sync turned off.");
- }
- return false;
- }
- if (getIsSyncable(target.account, target.userId, target.provider)
- == 0) {
- if (isLoggable) {
- Log.v(TAG, " Not scheduling periodic operation: isSyncable == 0.");
- }
- return false;
- }
- } else if (target.target_service) {
- if (mSyncStorageEngine.getIsTargetServiceActive(target.service, target.userId)) {
- if (isLoggable) {
- Log.v(TAG, " Not scheduling periodic operation: isEnabled == 0.");
- }
- return false;
- }
- }
- return true;
- }
-
- /**
- * Turn any periodic sync operations that are ready to run into pending sync operations.
- * @return the desired start time of the earliest future periodic sync operation,
- * in milliseconds since boot
- */
- private long scheduleReadyPeriodicSyncs() {
- final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
- if (isLoggable) {
- Log.v(TAG, "scheduleReadyPeriodicSyncs");
- }
- long earliestFuturePollTime = Long.MAX_VALUE;
-
- final long nowAbsolute = System.currentTimeMillis();
- final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
- ? (nowAbsolute - mSyncRandomOffsetMillis) : 0;
-
- ArrayList<Pair<AuthorityInfo, SyncStatusInfo>> infos = mSyncStorageEngine
- .getCopyOfAllAuthoritiesWithSyncStatus();
- for (Pair<AuthorityInfo, SyncStatusInfo> info : infos) {
- final AuthorityInfo authorityInfo = info.first;
- final SyncStatusInfo status = info.second;
-
- if (TextUtils.isEmpty(authorityInfo.target.provider)) {
- Log.e(TAG, "Got an empty provider string. Skipping: "
- + authorityInfo.target.provider);
- continue;
- }
-
- if (!isDispatchable(authorityInfo.target)) {
- continue;
- }
-
- for (int i = 0, N = authorityInfo.periodicSyncs.size(); i < N; i++) {
- final PeriodicSync sync = authorityInfo.periodicSyncs.get(i);
- final Bundle extras = sync.extras;
- final Long periodInMillis = sync.period * 1000;
- final Long flexInMillis = sync.flexTime * 1000;
- // Skip if the period is invalid.
- if (periodInMillis <= 0) {
- continue;
- }
- // Find when this periodic sync was last scheduled to run.
- final long lastPollTimeAbsolute = status.getPeriodicSyncTime(i);
- final long shiftedLastPollTimeAbsolute =
- (0 < lastPollTimeAbsolute - mSyncRandomOffsetMillis) ?
- (lastPollTimeAbsolute - mSyncRandomOffsetMillis) : 0;
- long remainingMillis
- = periodInMillis - (shiftedNowAbsolute % periodInMillis);
- long timeSinceLastRunMillis
- = (nowAbsolute - lastPollTimeAbsolute);
- // Schedule this periodic sync to run early if it's close enough to its next
- // runtime, and far enough from its last run time.
- // If we are early, there will still be time remaining in this period.
- boolean runEarly = remainingMillis <= flexInMillis
- && timeSinceLastRunMillis > periodInMillis - flexInMillis;
- if (isLoggable) {
- Log.v(TAG, "sync: " + i + " for " + authorityInfo.target + "."
- + " period: " + (periodInMillis)
- + " flex: " + (flexInMillis)
- + " remaining: " + (remainingMillis)
- + " time_since_last: " + timeSinceLastRunMillis
- + " last poll absol: " + lastPollTimeAbsolute
- + " last poll shifed: " + shiftedLastPollTimeAbsolute
- + " shifted now: " + shiftedNowAbsolute
- + " run_early: " + runEarly);
- }
- /*
- * Sync scheduling strategy: Set the next periodic sync
- * based on a random offset (in seconds). Also sync right
- * now if any of the following cases hold and mark it as
- * having been scheduled
- * Case 1: This sync is ready to run now.
- * Case 2: If the lastPollTimeAbsolute is in the
- * future, sync now and reinitialize. This can happen for
- * example if the user changed the time, synced and changed
- * back.
- * Case 3: If we failed to sync at the last scheduled time.
- * Case 4: This sync is close enough to the time that we can schedule it.
- */
- if (remainingMillis == periodInMillis // Case 1
- || lastPollTimeAbsolute > nowAbsolute // Case 2
- || timeSinceLastRunMillis >= periodInMillis // Case 3
- || runEarly) { // Case 4
- // Sync now
- SyncStorageEngine.EndPoint target = authorityInfo.target;
- final Pair<Long, Long> backoff =
- mSyncStorageEngine.getBackoff(target);
- mSyncStorageEngine.setPeriodicSyncTime(authorityInfo.ident,
- authorityInfo.periodicSyncs.get(i), nowAbsolute);
-
- if (target.target_provider) {
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType>
- syncAdapterInfo = mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(
- target.provider, target.account.type),
- target.userId);
- if (syncAdapterInfo == null) {
- continue;
- }
- scheduleSyncOperation(
- new SyncOperation(target.account, target.userId,
- syncAdapterInfo.uid,
- syncAdapterInfo.componentName.getPackageName(),
- SyncOperation.REASON_PERIODIC,
- SyncStorageEngine.SOURCE_PERIODIC,
- target.provider, extras,
- 0 /* runtime */, 0 /* flex */,
- backoff != null ? backoff.first : 0,
- mSyncStorageEngine.getDelayUntilTime(target),
- syncAdapterInfo.type.allowParallelSyncs()));
- } else if (target.target_service) {
- scheduleSyncOperation(
- new SyncOperation(target.service, target.userId,
- target.serviceUid, target.service.getPackageName(),
- SyncOperation.REASON_PERIODIC,
- SyncStorageEngine.SOURCE_PERIODIC,
- extras,
- 0 /* runtime */,
- 0 /* flex */,
- backoff != null ? backoff.first : 0,
- mSyncStorageEngine.getDelayUntilTime(target)));
- }
- }
- // Compute when this periodic sync should next run.
- long nextPollTimeAbsolute;
- if (runEarly) {
- // Add the time remaining so we don't get out of phase.
- nextPollTimeAbsolute = nowAbsolute + periodInMillis + remainingMillis;
+ deferSyncH(op, SYNC_DELAY_ON_CONFLICT);
+ return;
} else {
- nextPollTimeAbsolute = nowAbsolute + remainingMillis;
- }
- if (nextPollTimeAbsolute < earliestFuturePollTime) {
- earliestFuturePollTime = nextPollTimeAbsolute;
- }
- }
- }
-
- if (earliestFuturePollTime == Long.MAX_VALUE) {
- return Long.MAX_VALUE;
- }
-
- // convert absolute time to elapsed time
- return SystemClock.elapsedRealtime() +
- ((earliestFuturePollTime < nowAbsolute) ?
- 0 : (earliestFuturePollTime - nowAbsolute));
- }
-
- private long maybeStartNextSyncH() {
- final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
- if (isLoggable) Log.v(TAG, "maybeStartNextSync");
-
- // If we aren't ready to run (e.g. the data connection is down), get out.
- if (!mDataConnectionIsConnected) {
- if (isLoggable) {
- Log.v(TAG, "maybeStartNextSync: no data connection, skipping");
- }
- setSyncActive(false);
- return Long.MAX_VALUE;
- }
-
- if (mStorageIsLow) {
- if (isLoggable) {
- Log.v(TAG, "maybeStartNextSync: memory low, skipping");
- }
- setSyncActive(false);
- return Long.MAX_VALUE;
- }
-
- if (mDeviceIsIdle) {
- if (isLoggable) {
- Log.v(TAG, "maybeStartNextSync: device idle, skipping");
- }
- setSyncActive(false);
- return Long.MAX_VALUE;
- }
-
- // If the accounts aren't known yet then we aren't ready to run. We will be kicked
- // when the account lookup request does complete.
- if (mRunningAccounts == INITIAL_ACCOUNTS_ARRAY) {
- if (isLoggable) {
- Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
- }
- setSyncActive(false);
- return Long.MAX_VALUE;
- }
-
- // Otherwise consume SyncOperations from the head of the SyncQueue until one is
- // found that is runnable (not disabled, etc). If that one is ready to run then
- // start it, otherwise just get out.
- final long now = SystemClock.elapsedRealtime();
-
- // will be set to the next time that a sync should be considered for running
- long nextReadyToRunTime = Long.MAX_VALUE;
-
- // order the sync queue, dropping syncs that are not allowed
- ArrayList<SyncOperation> operations = new ArrayList<SyncOperation>();
- synchronized (mSyncQueue) {
- if (isLoggable) {
- Log.v(TAG, "build the operation array, syncQueue size is "
- + mSyncQueue.getOperations().size());
- }
- final Iterator<SyncOperation> operationIterator =
- mSyncQueue.getOperations().iterator();
-
- final ActivityManager am = mContext.getSystemService(ActivityManager.class);
- final Set<Integer> removedUsers = Sets.newHashSet();
- while (operationIterator.hasNext()) {
- final SyncOperation op = operationIterator.next();
-
- // If the user is not running unlocked, skip the request.
- if (!am.isUserRunningAndUnlocked(op.target.userId)) {
- final UserInfo userInfo = mUserManager.getUserInfo(op.target.userId);
- if (userInfo == null) {
- removedUsers.add(op.target.userId);
- }
if (isLoggable) {
- Log.v(TAG, " Dropping all sync operations for + "
- + op.target.userId + ": user not running unlocked.");
+ Slog.v(TAG, "Pushing back running sync due to a higher priority sync");
}
- continue;
- }
- String packageName = getPackageName(op.target);
- ApplicationInfo ai = null;
- if (packageName != null) {
- try {
- ai = mContext.getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_UNINSTALLED_PACKAGES
- | PackageManager.GET_DISABLED_COMPONENTS);
- } catch (NameNotFoundException e) {
- operationIterator.remove();
- mSyncStorageEngine.deleteFromPending(op.pendingOperation);
- continue;
- }
- }
- // If app is considered idle, then skip for now and backoff
- if (ai != null
- && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) {
- increaseBackoffSetting(op);
- op.appIdle = true;
- if (isLoggable) {
- Log.v(TAG, "Sync backing off idle app " + packageName);
- }
- continue;
- } else {
- op.appIdle = false;
- }
- if (!isOperationValidLocked(op)) {
- operationIterator.remove();
- mSyncStorageEngine.deleteFromPending(op.pendingOperation);
- continue;
- }
- // If the next run time is in the future, even given the flexible scheduling,
- // return the time.
- if (op.effectiveRunTime - op.flexTime > now) {
- if (nextReadyToRunTime > op.effectiveRunTime) {
- nextReadyToRunTime = op.effectiveRunTime;
- }
- if (isLoggable) {
- Log.v(TAG, " Not running sync operation: Sync too far in future."
- + "effective: " + op.effectiveRunTime + " flex: " + op.flexTime
- + " now: " + now);
- }
- continue;
- }
- // Add this sync to be run.
- operations.add(op);
- }
-
- for (Integer user : removedUsers) {
- // if it's still removed
- if (mUserManager.getUserInfo(user) == null) {
- onUserRemoved(user);
+ deferActiveSyncH(asc);
}
}
}
- // find the next operation to dispatch, if one is ready
- // iterate from the top, keep issuing (while potentially canceling existing syncs)
- // until the quotas are filled.
- // once the quotas are filled iterate once more to find when the next one would be
- // (also considering pre-emption reasons).
- if (isLoggable) Log.v(TAG, "sort the candidate operations, size " + operations.size());
- Collections.sort(operations);
- if (isLoggable) Log.v(TAG, "dispatch all ready sync operations");
- for (int i = 0, N = operations.size(); i < N; i++) {
- final SyncOperation candidate = operations.get(i);
- final boolean candidateIsInitialization = candidate.isInitialization();
-
- int numInit = 0;
- int numRegular = 0;
- ActiveSyncContext conflict = null;
- ActiveSyncContext longRunning = null;
- ActiveSyncContext toReschedule = null;
- ActiveSyncContext oldestNonExpeditedRegular = null;
-
- for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
- final SyncOperation activeOp = activeSyncContext.mSyncOperation;
- if (activeOp.isInitialization()) {
- numInit++;
- } else {
- numRegular++;
- if (!activeOp.isExpedited()) {
- if (oldestNonExpeditedRegular == null
- || (oldestNonExpeditedRegular.mStartTime
- > activeSyncContext.mStartTime)) {
- oldestNonExpeditedRegular = activeSyncContext;
- }
- }
- }
- if (activeOp.isConflict(candidate)) {
- conflict = activeSyncContext;
- // don't break out since we want to do a full count of the varieties.
- } else {
- if (candidateIsInitialization == activeOp.isInitialization()
- && activeSyncContext.mStartTime + MAX_TIME_PER_SYNC < now) {
- longRunning = activeSyncContext;
- // don't break out since we want to do a full count of the varieties
- }
- }
+ if (isOperationValid(op)) {
+ if (!dispatchSyncOperation(op)) {
+ mSyncJobService.callJobFinished(op.jobId, false);
}
-
- if (isLoggable) {
- Log.v(TAG, "candidate " + (i + 1) + " of " + N + ": " + candidate);
- Log.v(TAG, " numActiveInit=" + numInit + ", numActiveRegular=" + numRegular);
- Log.v(TAG, " longRunning: " + longRunning);
- Log.v(TAG, " conflict: " + conflict);
- Log.v(TAG, " oldestNonExpeditedRegular: " + oldestNonExpeditedRegular);
- }
-
- final boolean roomAvailable = candidateIsInitialization
- ? numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS
- : numRegular < MAX_SIMULTANEOUS_REGULAR_SYNCS;
-
- if (conflict != null) {
- if (candidateIsInitialization && !conflict.mSyncOperation.isInitialization()
- && numInit < MAX_SIMULTANEOUS_INITIALIZATION_SYNCS) {
- toReschedule = conflict;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "canceling and rescheduling sync since an initialization "
- + "takes higher priority, " + conflict);
- }
- } else if (candidate.isExpedited() && !conflict.mSyncOperation.isExpedited()
- && (candidateIsInitialization
- == conflict.mSyncOperation.isInitialization())) {
- toReschedule = conflict;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "canceling and rescheduling sync since an expedited "
- + "takes higher priority, " + conflict);
- }
- } else {
- continue;
- }
- } else if (roomAvailable) {
- // dispatch candidate
- } else if (candidate.isExpedited() && oldestNonExpeditedRegular != null
- && !candidateIsInitialization) {
- // We found an active, non-expedited regular sync. We also know that the
- // candidate doesn't conflict with this active sync since conflict
- // is null. Reschedule the active sync and start the candidate.
- toReschedule = oldestNonExpeditedRegular;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "canceling and rescheduling sync since an expedited is ready to"
- + " run, " + oldestNonExpeditedRegular);
- }
- } else if (longRunning != null
- && (candidateIsInitialization
- == longRunning.mSyncOperation.isInitialization())) {
- // We found an active, long-running sync. Reschedule the active
- // sync and start the candidate.
- toReschedule = longRunning;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "canceling and rescheduling sync since it ran roo long, "
- + longRunning);
- }
- } else {
- // we were unable to find or make space to run this candidate, go on to
- // the next one
- continue;
- }
-
- if (toReschedule != null) {
- runSyncFinishedOrCanceledH(null, toReschedule);
- scheduleSyncOperation(toReschedule.mSyncOperation);
- }
- synchronized (mSyncQueue) {
- mSyncQueue.remove(candidate);
- }
- dispatchSyncOperation(candidate);
+ } else {
+ mSyncJobService.callJobFinished(op.jobId, false);
}
-
- setSyncActive(mActiveSyncContexts.size() > 0);
-
- return nextReadyToRunTime;
+ setAuthorityPendingState(op.target);
}
- void setSyncActive(boolean active) {
- if (mLocalDeviceIdleController == null) {
- mLocalDeviceIdleController
- = LocalServices.getService(DeviceIdleController.LocalService.class);
- }
- if (mLocalDeviceIdleController != null) {
- if (mReportedSyncActive != active) {
- mReportedSyncActive = active;
- mLocalDeviceIdleController.setSyncActive(active);
+ private ActiveSyncContext findActiveSyncContextH(int jobId) {
+ for (ActiveSyncContext asc: mActiveSyncContexts) {
+ SyncOperation op = asc.mSyncOperation;
+ if (op != null && op.jobId == jobId) {
+ return asc;
}
}
+ return null;
}
- private void updateRunningAccountsH() {
+ private void updateRunningAccountsH(EndPoint syncTargets) {
mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
-
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "Accounts list: ");
+ for (AccountAndUser acc : mRunningAccounts) {
+ Slog.v(TAG, acc.toString());
+ }
+ }
if (mBootCompleted) {
doDatabaseCleanup();
+ mSyncStorageEngine.restoreAllPeriodicSyncs();
}
AccountAndUser[] accounts = mRunningAccounts;
@@ -2886,9 +2527,119 @@
null /* no result since this is a cancel */);
}
}
- // we must do this since we don't bother scheduling alarms when
- // the accounts are not set yet
- sendCheckAlarmsMessage();
+
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ for (SyncOperation op: ops) {
+ if (!containsAccountAndUser(accounts, op.target.account, op.target.userId)) {
+ removeSyncOperationFromCache(op.jobId);
+ getJobScheduler().cancel(op.jobId);
+ }
+ }
+
+ if (syncTargets != null) {
+ scheduleSync(syncTargets.account, syncTargets.userId,
+ SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider, null, 0, 0,
+ true);
+ }
+ }
+
+ /**
+ * The given SyncOperation will be removed and a new one scheduled in its place if
+ * an updated period or flex is specified.
+ * @param syncOperation SyncOperation whose period and flex is to be updated.
+ * @param pollFrequencyMillis new period in milliseconds.
+ * @param flexMillis new flex time in milliseconds.
+ */
+ private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis,
+ long flexMillis) {
+ if (!(pollFrequencyMillis == syncOperation.periodMillis
+ && flexMillis == syncOperation.flexMillis)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "updating period " + syncOperation + " to " + pollFrequencyMillis
+ + " and flex to " + flexMillis);
+ }
+ removePeriodicSyncInternalH(syncOperation);
+ syncOperation.periodMillis = pollFrequencyMillis;
+ syncOperation.flexMillis = flexMillis;
+ scheduleSyncOperationH(syncOperation);
+ }
+ }
+
+ private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex,
+ Bundle extras) {
+ final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+ verifyJobScheduler(); // Will fill in mScheduledSyncs cache if it is not already filled.
+ final long pollFrequencyMillis = pollFrequency * 1000L;
+ final long flexMillis = flex * 1000L;
+ if (isLoggable) {
+ Slog.v(TAG, "Addition to periodic syncs requested: " + target
+ + " period: " + pollFrequency
+ + " flexMillis: " + flex
+ + " extras: " + extras.toString());
+ }
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ for (SyncOperation op: ops) {
+ if (op.isPeriodic && op.target.matchesSpec(target)
+ && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
+ maybeUpdateSyncPeriodH(op, pollFrequencyMillis, flexMillis);
+ return;
+ }
+ }
+
+ if (isLoggable) {
+ Slog.v(TAG, "Adding new periodic sync: " + target
+ + " period: " + pollFrequency
+ + " flexMillis: " + flex
+ + " extras: " + extras.toString());
+ }
+
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType>
+ syncAdapterInfo = mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(
+ target.provider, target.account.type),
+ target.userId);
+ if (syncAdapterInfo == null) {
+ return;
+ }
+
+ 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;
+ scheduleSyncOperationH(op);
+ mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ }
+
+ /**
+ * Remove this periodic sync operation and all one-off operations initiated by it.
+ */
+ private void removePeriodicSyncInternalH(SyncOperation syncOperation) {
+ // Remove this periodic sync and all one-off syncs initiated by it.
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ for (SyncOperation op: ops) {
+ if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) {
+ ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId);
+ if (asc != null) {
+ mSyncJobService.callJobFinished(syncOperation.jobId, false);
+ runSyncFinishedOrCanceledH(null, asc);
+ }
+ removeSyncOperationFromCache(op.jobId);
+ getJobScheduler().cancel(op.jobId);
+ }
+ }
+ }
+
+ private void removePeriodicSyncH(EndPoint target, Bundle extras) {
+ verifyJobScheduler();
+ List<SyncOperation> ops = getAllPendingSyncsFromCache();
+ for (SyncOperation op: ops) {
+ if (op.isPeriodic && op.target.matchesSpec(target)
+ && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
+ removePeriodicSyncInternalH(op);
+ }
+ }
}
private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) {
@@ -2906,116 +2657,52 @@
remainder %= 1024;
long b = remainder;
Log.d(TAG, String.format(
- "Time since last update: %ds. Delta transferred: %dMBs,%dKBs,%dBs",
- (SystemClock.elapsedRealtime()
- - activeSyncContext.mLastPolledTimeElapsed)/1000,
- mb, kb, b)
+ "Time since last update: %ds. Delta transferred: %dMBs,%dKBs,%dBs",
+ (SystemClock.elapsedRealtime()
+ - activeSyncContext.mLastPolledTimeElapsed)/1000,
+ mb, kb, b)
);
}
return (deltaBytesTransferred <= SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES);
}
/**
- * Determine if a sync is no longer valid and should be dropped from the sync queue and its
- * pending op deleted.
- * @param op operation for which the sync is to be scheduled.
+ * Determine if a sync is no longer valid and should be dropped.
*/
- private boolean isOperationValidLocked(SyncOperation op) {
+ private boolean isOperationValid(SyncOperation op) {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
- int targetUid;
int state;
- final SyncStorageEngine.EndPoint target = op.target;
+ final EndPoint target = op.target;
boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId);
- if (target.target_provider) {
- // Drop the sync if the account of this operation no longer exists.
- AccountAndUser[] accounts = mRunningAccounts;
- if (!containsAccountAndUser(accounts, target.account, target.userId)) {
- if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: account doesn't exist.");
- }
- return false;
+ // Drop the sync if the account of this operation no longer exists.
+ AccountAndUser[] accounts = mRunningAccounts;
+ if (!containsAccountAndUser(accounts, target.account, target.userId)) {
+ if (isLoggable) {
+ Slog.v(TAG, " Dropping sync operation: account doesn't exist.");
}
- // Drop this sync request if it isn't syncable.
- state = getIsSyncable(target.account, target.userId, target.provider);
- if (state == 0) {
- if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: isSyncable == 0.");
- }
- return false;
- }
- syncEnabled = syncEnabled && mSyncStorageEngine.getSyncAutomatically(
- target.account, target.userId, target.provider);
-
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
- syncAdapterInfo = mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(
- target.provider, target.account.type), target.userId);
- if (syncAdapterInfo != null) {
- targetUid = syncAdapterInfo.uid;
- } else {
- if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: No sync adapter registered"
- + "for: " + target);
- }
- return false;
- }
- } else if (target.target_service) {
- state = mSyncStorageEngine.getIsTargetServiceActive(target.service, target.userId)
- ? 1 : 0;
- if (state == 0) {
- // TODO: Change this to not drop disabled syncs - keep them in the pending queue.
- if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: isActive == 0.");
- }
- return false;
- }
- try {
- targetUid = mContext.getPackageManager()
- .getServiceInfo(target.service, 0)
- .applicationInfo
- .uid;
- } catch (PackageManager.NameNotFoundException e) {
- if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: No service registered for: "
- + target.service);
- }
- return false;
- }
- } else {
- Log.e(TAG, "Unknown target for Sync Op: " + target);
return false;
}
+ // Drop this sync request if it isn't syncable.
+ state = getIsSyncable(target.account, target.userId, target.provider);
+ if (state == 0) {
+ if (isLoggable) {
+ Slog.v(TAG, " Dropping sync operation: isSyncable == 0.");
+ }
+ return false;
+ }
+ syncEnabled = syncEnabled && mSyncStorageEngine.getSyncAutomatically(
+ target.account, target.userId, target.provider);
// We ignore system settings that specify the sync is invalid if:
// 1) It's manual - we try it anyway. When/if it fails it will be rescheduled.
// or
// 2) it's an initialisation sync - we just need to connect to it.
- final boolean ignoreSystemConfiguration =
- op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
- || (state < 0);
+ final boolean ignoreSystemConfiguration = op.isIgnoreSettings() || (state < 0);
// Sync not enabled.
if (!syncEnabled && !ignoreSystemConfiguration) {
if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: disallowed by settings/network.");
- }
- return false;
- }
- // Network down.
- final NetworkInfo networkInfo = getConnectivityManager()
- .getActiveNetworkInfoForUid(targetUid);
- final boolean uidNetworkConnected = networkInfo != null && networkInfo.isConnected();
- if (!uidNetworkConnected && !ignoreSystemConfiguration) {
- if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: disallowed by settings/network.");
- }
- return false;
- }
- // Metered network.
- if (op.isNotAllowedOnMetered() && getConnectivityManager().isActiveNetworkMetered()
- && !ignoreSystemConfiguration) {
- if (isLoggable) {
- Log.v(TAG, " Dropping sync operation: not allowed on metered network.");
+ Slog.v(TAG, " Dropping sync operation: disallowed by settings/network.");
}
return false;
}
@@ -3024,65 +2711,42 @@
private boolean dispatchSyncOperation(SyncOperation op) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
- Log.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
+ Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
+ Slog.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
for (ActiveSyncContext syncContext : mActiveSyncContexts) {
- Log.v(TAG, syncContext.toString());
+ Slog.v(TAG, syncContext.toString());
}
}
// Connect to the sync adapter.
int targetUid;
ComponentName targetComponent;
final SyncStorageEngine.EndPoint info = op.target;
- if (info.target_provider) {
- SyncAdapterType syncAdapterType =
- SyncAdapterType.newKey(info.provider, info.account.type);
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
- syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId);
- if (syncAdapterInfo == null) {
- Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
- + ", removing settings for it");
- mSyncStorageEngine.removeAuthority(info);
- return false;
- }
- targetUid = syncAdapterInfo.uid;
- targetComponent = syncAdapterInfo.componentName;
- } else {
- // TODO: Store the uid of the service as part of the authority info in order to
- // avoid this call?
- try {
- targetUid = mContext.getPackageManager()
- .getServiceInfo(info.service, 0)
- .applicationInfo
- .uid;
- targetComponent = info.service;
- } catch(PackageManager.NameNotFoundException e) {
- Log.d(TAG, "Can't find a service for " + info.service
- + ", removing settings for it");
- mSyncStorageEngine.removeAuthority(info);
- return false;
- }
+ SyncAdapterType syncAdapterType =
+ SyncAdapterType.newKey(info.provider, info.account.type);
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
+ syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId);
+ if (syncAdapterInfo == null) {
+ Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
+ + ", removing settings for it");
+ mSyncStorageEngine.removeAuthority(info);
+ return false;
}
+ targetUid = syncAdapterInfo.uid;
+ targetComponent = syncAdapterInfo.componentName;
ActiveSyncContext activeSyncContext =
new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
+ Slog.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
}
activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
mActiveSyncContexts.add(activeSyncContext);
- // Post message to cancel this sync if it runs for too long.
- if (!activeSyncContext.mSyncOperation.isExpedited() &&
- !activeSyncContext.mSyncOperation.isManual() &&
- !activeSyncContext.mSyncOperation.isIgnoreSettings()) {
- postSyncExpiryMessage(activeSyncContext);
- }
// Post message to begin monitoring this sync's progress.
postMonitorSyncProgressMessage(activeSyncContext);
if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) {
- Log.e(TAG, "Bind attempt failed - target: " + targetComponent);
+ Slog.e(TAG, "Bind attempt failed - target: " + targetComponent);
closeActiveSyncContext(activeSyncContext);
return false;
}
@@ -3090,33 +2754,25 @@
return true;
}
- private void runBoundToAdapter(final ActiveSyncContext activeSyncContext,
- IBinder syncAdapter) {
+ private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext,
+ IBinder syncAdapter) {
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
try {
activeSyncContext.mIsLinkedToDeath = true;
syncAdapter.linkToDeath(activeSyncContext, 0);
- if (syncOperation.target.target_provider) {
- activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter);
- activeSyncContext.mSyncAdapter
+ activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter);
+ activeSyncContext.mSyncAdapter
.startSync(activeSyncContext, syncOperation.target.provider,
syncOperation.target.account, syncOperation.extras);
- } else if (syncOperation.target.target_service) {
- activeSyncContext.mSyncServiceAdapter =
- ISyncServiceAdapter.Stub.asInterface(syncAdapter);
- activeSyncContext.mSyncServiceAdapter
- .startSync(activeSyncContext, syncOperation.extras);
- }
} catch (RemoteException remoteExc) {
Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
closeActiveSyncContext(activeSyncContext);
- increaseBackoffSetting(syncOperation);
- scheduleSyncOperation(
- new SyncOperation(syncOperation, 0L /* newRunTimeFromNow */));
+ increaseBackoffSetting(syncOperation.target);
+ scheduleSyncOperationH(syncOperation);
} catch (RuntimeException exc) {
closeActiveSyncContext(activeSyncContext);
- Log.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
+ Slog.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
}
}
@@ -3141,25 +2797,31 @@
false /* no config settings */)) {
continue;
}
+ mSyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false);
runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext);
}
}
}
+ /**
+ * 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);
+ }
+
private void runSyncFinishedOrCanceledH(SyncResult syncResult,
ActiveSyncContext activeSyncContext) {
- boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
+ final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
final SyncStorageEngine.EndPoint info = syncOperation.target;
if (activeSyncContext.mIsLinkedToDeath) {
- if (info.target_provider) {
- activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
- } else {
- activeSyncContext.mSyncServiceAdapter.asBinder()
- .unlinkToDeath(activeSyncContext, 0);
- }
+ activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
activeSyncContext.mIsLinkedToDeath = false;
}
closeActiveSyncContext(activeSyncContext);
@@ -3169,7 +2831,7 @@
int upstreamActivity;
if (syncResult != null) {
if (isLoggable) {
- Log.v(TAG, "runSyncFinishedOrCanceled [finished]: "
+ Slog.v(TAG, "runSyncFinishedOrCanceled [finished]: "
+ syncOperation + ", result " + syncResult);
}
@@ -3178,23 +2840,35 @@
// TODO: set these correctly when the SyncResult is extended to include it
downstreamActivity = 0;
upstreamActivity = 0;
- clearBackoffSetting(syncOperation);
+ clearBackoffSetting(syncOperation.target);
+
+ // If the operation completes successfully and it was scheduled due to
+ // a periodic operation failing, we reschedule the periodic operation to
+ // start from now.
+ if (syncOperation.isDerivedFromFailedPeriodicSync()) {
+ reschedulePeriodicSyncH(syncOperation);
+ }
} else {
Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
// the operation failed so increase the backoff time
- increaseBackoffSetting(syncOperation);
- // reschedule the sync if so indicated by the syncResult
- maybeRescheduleSync(syncResult, syncOperation);
+ increaseBackoffSetting(syncOperation.target);
+ if (!syncOperation.isPeriodic) {
+ // reschedule the sync if so indicated by the syncResult
+ maybeRescheduleSync(syncResult, syncOperation);
+ } else {
+ // create a normal sync instance that will respect adapter backoffs
+ postScheduleSyncMessage(syncOperation.createOneTimeSyncOperation());
+ }
historyMessage = ContentResolver.syncErrorToString(
syncResultToErrorNumber(syncResult));
// TODO: set these correctly when the SyncResult is extended to include it
downstreamActivity = 0;
upstreamActivity = 0;
}
- setDelayUntilTime(syncOperation, syncResult.delayUntil);
+ setDelayUntilTime(syncOperation.target, syncResult.delayUntil);
} else {
if (isLoggable) {
- Log.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation);
+ Slog.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation);
}
if (activeSyncContext.mSyncAdapter != null) {
try {
@@ -3202,12 +2876,6 @@
} catch (RemoteException e) {
// we don't need to retry this in this case
}
- } else if (activeSyncContext.mSyncServiceAdapter != null) {
- try {
- activeSyncContext.mSyncServiceAdapter.cancelSync(activeSyncContext);
- } catch (RemoteException e) {
- // we don't need to retry this in this case
- }
}
historyMessage = SyncStorageEngine.MESG_CANCELED;
downstreamActivity = 0;
@@ -3217,38 +2885,23 @@
stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
upstreamActivity, downstreamActivity, elapsedTime);
// Check for full-resync and schedule it after closing off the last sync.
- if (info.target_provider) {
- if (syncResult != null && syncResult.tooManyDeletions) {
- installHandleTooManyDeletesNotification(info.account,
- info.provider, syncResult.stats.numDeletes,
- info.userId);
- } else {
- mNotificationMgr.cancelAsUser(null,
- info.account.hashCode() ^ info.provider.hashCode(),
- new UserHandle(info.userId));
- }
- if (syncResult != null && syncResult.fullSyncRequested) {
- scheduleSyncOperation(
- new SyncOperation(info.account, info.userId,
- syncOperation.owningUid, syncOperation.owningPackage,
+ if (syncResult != null && syncResult.tooManyDeletions) {
+ installHandleTooManyDeletesNotification(info.account,
+ info.provider, syncResult.stats.numDeletes,
+ info.userId);
+ } else {
+ mNotificationMgr.cancelAsUser(null,
+ info.account.hashCode() ^ info.provider.hashCode(),
+ new UserHandle(info.userId));
+ }
+ if (syncResult != null && syncResult.fullSyncRequested) {
+ scheduleSyncOperationH(
+ new SyncOperation(info.account, info.userId,
+ syncOperation.owningUid, syncOperation.owningPackage,
syncOperation.reason,
syncOperation.syncSource, info.provider, new Bundle(),
- 0 /* delay */, 0 /* flex */,
- syncOperation.backoff, syncOperation.delayUntil,
syncOperation.allowParallelSyncs));
- }
- } else {
- if (syncResult != null && syncResult.fullSyncRequested) {
- scheduleSyncOperation(
- new SyncOperation(info.service, info.userId,
- syncOperation.owningUid, syncOperation.owningPackage,
- syncOperation.reason,
- syncOperation.syncSource, new Bundle(),
- 0 /* delay */, 0 /* flex */,
- syncOperation.backoff, syncOperation.delayUntil));
- }
}
- // no need to schedule an alarm, as that will be done by our caller.
}
private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) {
@@ -3258,7 +2911,7 @@
activeSyncContext.mSyncOperation.target.userId);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for "
+ Slog.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for "
+ activeSyncContext.toString());
}
mSyncHandler.removeMessages(SyncHandler.MESSAGE_SYNC_EXPIRED, activeSyncContext);
@@ -3294,82 +2947,8 @@
throw new IllegalStateException("we are not in an error state, " + syncResult);
}
- private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime,
- long nextPendingEventElapsedTime) {
- // in each of these cases the sync loop will be kicked, which will cause this
- // method to be called again
- if (!mDataConnectionIsConnected) return;
- if (mStorageIsLow) return;
- if (mDeviceIsIdle) return;
-
- // When we should consider canceling an active sync
- long earliestTimeoutTime = Long.MAX_VALUE;
- for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
- final long currentSyncTimeoutTime =
- currentSyncContext.mTimeoutStartTime + MAX_TIME_PER_SYNC;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "manageSyncAlarm: active sync, mTimeoutStartTime + MAX is "
- + currentSyncTimeoutTime);
- }
- if (earliestTimeoutTime > currentSyncTimeoutTime) {
- earliestTimeoutTime = currentSyncTimeoutTime;
- }
- }
-
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime);
- Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is "
- + nextPeriodicEventElapsedTime);
- Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is "
- + nextPendingEventElapsedTime);
- }
-
- long alarmTime = Math.min(earliestTimeoutTime, nextPeriodicEventElapsedTime);
- alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime);
-
- // Bound the alarm time.
- final long now = SystemClock.elapsedRealtime();
- if (alarmTime < now + SYNC_ALARM_TIMEOUT_MIN) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "manageSyncAlarm: the alarmTime is too small, "
- + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN));
- }
- alarmTime = now + SYNC_ALARM_TIMEOUT_MIN;
- }
-
- // Determine if we need to set or cancel the alarm
- boolean shouldSet = false;
- boolean shouldCancel = false;
- final boolean alarmIsActive = (mAlarmScheduleTime != null) && (now < mAlarmScheduleTime);
-
- if (alarmTime != Long.MAX_VALUE) {
- // Need the alarm if it isn't set or has changed.
- if (!alarmIsActive || alarmTime != mAlarmScheduleTime) {
- shouldSet = true;
- }
- } else {
- shouldCancel = alarmIsActive;
- }
-
- // Set or cancel the alarm as directed.
- ensureAlarmService();
- if (shouldSet) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "requesting that the alarm manager wake us up at elapsed time "
- + alarmTime + ", now is " + now + ", " + ((alarmTime - now) / 1000)
- + " secs from now");
- }
- mAlarmScheduleTime = alarmTime;
- mAlarmService.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime,
- mSyncAlarmIntent);
- } else if (shouldCancel) {
- mAlarmScheduleTime = null;
- mAlarmService.cancel(mSyncAlarmIntent);
- }
- }
-
private void installHandleTooManyDeletesNotification(Account account, String authority,
- long numDeletes, int userId) {
+ long numDeletes, int userId) {
if (mNotificationMgr == null) return;
final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
@@ -3445,7 +3024,7 @@
}
public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
- int upstreamActivity, int downstreamActivity, long elapsedTime) {
+ int upstreamActivity, int downstreamActivity, long elapsedTime) {
EventLog.writeEvent(2720,
syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
@@ -3453,21 +3032,6 @@
}
}
- String getPackageName(EndPoint endpoint) {
- if (endpoint.target_service) {
- return endpoint.service.getPackageName();
- } else {
- SyncAdapterType syncAdapterType =
- SyncAdapterType.newKey(endpoint.provider, endpoint.account.type);
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
- syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, endpoint.userId);
- if (syncAdapterInfo == null) {
- return null;
- }
- return syncAdapterInfo.componentName.getPackageName();
- }
- }
-
private boolean isSyncStillActiveH(ActiveSyncContext activeSyncContext) {
for (ActiveSyncContext sync : mActiveSyncContexts) {
if (sync == activeSyncContext) {
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index ab777ae..4fb31c0 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -18,22 +18,27 @@
import android.accounts.Account;
import android.content.pm.PackageManager;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.os.Bundle;
-import android.os.SystemClock;
+import android.os.PersistableBundle;
import android.os.UserHandle;
-import android.util.Log;
+import android.util.Slog;
/**
* Value type that represents a sync operation.
- * TODO: This is the class to flesh out with all the scheduling data - metered/unmetered,
- * transfer-size, etc.
+ * This holds all information related to a sync operation - both one off and periodic.
+ * Data stored in this is used to schedule a job with the JobScheduler.
* {@hide}
*/
-public class SyncOperation implements Comparable {
+public class SyncOperation {
public static final String TAG = "SyncManager";
+ /**
+ * This is used in the {@link #sourcePeriodicId} field if the operation is not initiated by a failed
+ * periodic sync.
+ */
+ public static final int NO_JOB_ID = -1;
+
public static final int REASON_BACKGROUND_DATA_SETTINGS_CHANGED = -1;
public static final int REASON_ACCOUNTS_UPDATED = -2;
public static final int REASON_SERVICE_CHANGED = -3;
@@ -57,10 +62,6 @@
"UserStart",
};
- public static final int SYNC_TARGET_UNKNOWN = 0;
- public static final int SYNC_TARGET_ADAPTER = 1;
- public static final int SYNC_TARGET_SERVICE = 2;
-
/** Identifying info for the target for this operation. */
public final SyncStorageEngine.EndPoint target;
public final int owningUid;
@@ -70,51 +71,44 @@
/** Where this sync was initiated. */
public final int syncSource;
public final boolean allowParallelSyncs;
+ public final Bundle extras;
+ public final boolean isPeriodic;
+ /** jobId of the periodic SyncOperation that initiated this one */
+ public final int sourcePeriodicId;
+ /** Operations are considered duplicates if keys are equal */
public final String key;
- /** Internal boolean to avoid reading a bundle everytime we want to compare operations. */
- private final boolean expedited;
- public Bundle extras;
- /** Bare-bones version of this operation that is persisted across reboots. */
- public SyncStorageEngine.PendingOperation pendingOperation;
- /** Elapsed real time in millis at which to run this sync. */
- public long latestRunTime;
- /** Set by the SyncManager in order to delay retries. */
- public long backoff;
- /** Specified by the adapter to delay subsequent sync operations. */
- public long delayUntil;
- /**
- * Elapsed real time in millis when this sync will be run.
- * Depends on max(backoff, latestRunTime, and delayUntil).
- */
- public long effectiveRunTime;
- /** Amount of time before {@link #effectiveRunTime} from which this sync can run. */
- public long flexTime;
+ /** Poll frequency of periodic sync in milliseconds */
+ public long periodMillis;
+ /** Flex time of periodic sync in milliseconds */
+ public long flexMillis;
/** Descriptive string key for this operation */
public String wakeLockName;
+ /**
+ * Used when duplicate pending syncs are present. The one with the lowest expectedRuntime
+ * is kept, others are discarded.
+ */
+ public long expectedRuntime;
- /** Whether this sync op was recently skipped due to the app being idle */
- public boolean appIdle;
+ /** jobId of the JobScheduler job corresponding to this sync */
+ public int jobId;
public SyncOperation(Account account, int userId, int owningUid, String owningPackage,
- int reason, int source, String provider, Bundle extras, long runTimeFromNow,
- long flexTime, long backoff, long delayUntil, boolean allowParallelSyncs) {
+ int reason, int source, String provider, Bundle extras,
+ boolean allowParallelSyncs) {
this(new SyncStorageEngine.EndPoint(account, provider, userId), owningUid, owningPackage,
- reason, source, extras, runTimeFromNow, flexTime, backoff, delayUntil,
- allowParallelSyncs);
- }
-
- public SyncOperation(ComponentName service, int userId, int owningUid, String owningPackage,
- int reason, int source, Bundle extras, long runTimeFromNow, long flexTime, long backoff,
- long delayUntil) {
- this(new SyncStorageEngine.EndPoint(service, userId, owningUid), owningUid, owningPackage,
- reason, source, extras, runTimeFromNow, flexTime, backoff, delayUntil,
- true /* allowParallelSyncs */);
+ reason, source, extras, allowParallelSyncs);
}
private SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
- int reason, int source, Bundle extras, long runTimeFromNow, long flexTime,
- long backoff, long delayUntil, boolean allowParallelSyncs) {
+ int reason, int source, Bundle extras, boolean allowParallelSyncs) {
+ this(info, owningUid, owningPackage, reason, source, extras, allowParallelSyncs, false,
+ NO_JOB_ID);
+ }
+
+ public SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
+ int reason, int source, Bundle extras, boolean allowParallelSyncs,
+ boolean isPeriodic, int sourcePeriodicId) {
this.target = info;
this.owningUid = owningUid;
this.owningPackage = owningPackage;
@@ -122,43 +116,173 @@
this.syncSource = source;
this.extras = new Bundle(extras);
cleanBundle(this.extras);
- this.delayUntil = delayUntil;
- this.backoff = backoff;
this.allowParallelSyncs = allowParallelSyncs;
- final long now = SystemClock.elapsedRealtime();
- // Set expedited based on runTimeFromNow. The SyncManager specifies whether the op is
- // expedited (Not done solely based on bundle).
- if (runTimeFromNow < 0) {
- this.expedited = true;
- // Sanity check: Will always be true.
- if (!this.extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
- this.extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- }
- this.latestRunTime = now;
- this.flexTime = 0;
- } else {
- this.expedited = false;
- this.extras.remove(ContentResolver.SYNC_EXTRAS_EXPEDITED);
- this.latestRunTime = now + runTimeFromNow;
- this.flexTime = flexTime;
+ this.isPeriodic = isPeriodic;
+ this.sourcePeriodicId = sourcePeriodicId;
+ this.key = toKey(target, extras);
+ }
+
+ /* Get a one off sync operation instance from a periodic sync. */
+ public SyncOperation createOneTimeSyncOperation() {
+ if (!isPeriodic) {
+ return null;
}
- updateEffectiveRunTime();
- this.key = toKey(info, this.extras);
+ 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;
}
- /** Used to reschedule a sync at a new point in time. */
- public SyncOperation(SyncOperation other, long newRunTimeFromNow) {
- this(other.target, other.owningUid, other.owningPackage, other.reason, other.syncSource,
- new Bundle(other.extras),
- newRunTimeFromNow,
- 0L /* In back-off so no flex */,
- other.backoff,
- other.delayUntil,
- other.allowParallelSyncs);
+ 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;
+ return op;
}
- public boolean matchesAuthority(SyncOperation other) {
- return this.target.matchesSpec(other.target);
+ public SyncOperation(SyncOperation other) {
+ target = other.target;
+ owningUid = other.owningUid;
+ owningPackage = other.owningPackage;
+ reason = other.reason;
+ syncSource = other.syncSource;
+ allowParallelSyncs = other.allowParallelSyncs;
+ extras = new Bundle(other.extras);
+ wakeLockName = other.wakeLockName();
+ isPeriodic = other.isPeriodic;
+ sourcePeriodicId = other.sourcePeriodicId;
+ periodMillis = other.periodMillis;
+ flexMillis = other.flexMillis;
+ this.key = other.key;
+ }
+
+ /**
+ * All fields are stored in a corresponding key in the persistable bundle.
+ *
+ * {@link #extras} is a Bundle and can contain parcelable objects. But only the type Account
+ * is allowed {@link ContentResolver#validateSyncExtrasBundle(Bundle)} that can't be stored in
+ * a PersistableBundle. For every value of type Account with key 'key', we store a
+ * PersistableBundle containing account information at key 'ACCOUNT:key'. The Account object
+ * can be reconstructed using this.
+ *
+ * We put a flag with key 'SyncManagerJob', to identify while reconstructing a sync operation
+ * from a bundle whether the bundle actually contains information about a sync.
+ * @return A persistable bundle containing all information to re-construct the sync operation.
+ */
+ PersistableBundle toJobInfoExtras() {
+ // This will be passed as extras bundle to a JobScheduler job.
+ PersistableBundle jobInfoExtras = new PersistableBundle();
+
+ PersistableBundle syncExtrasBundle = new PersistableBundle();
+ for (String key: extras.keySet()) {
+ Object value = extras.get(key);
+ if (value instanceof Account) {
+ Account account = (Account) value;
+ PersistableBundle accountBundle = new PersistableBundle();
+ accountBundle.putString("accountName", account.name);
+ accountBundle.putString("accountType", account.type);
+ // This is stored in jobInfoExtras so that we don't override a user specified
+ // sync extra with the same key.
+ jobInfoExtras.putPersistableBundle("ACCOUNT:" + key, accountBundle);
+ } else if (value instanceof Long) {
+ syncExtrasBundle.putLong(key, (Long) value);
+ } else if (value instanceof Integer) {
+ syncExtrasBundle.putInt(key, (Integer) value);
+ } else if (value instanceof Boolean) {
+ syncExtrasBundle.putBoolean(key, (Boolean) value);
+ } else if (value instanceof Float) {
+ syncExtrasBundle.putDouble(key, (Double) value);
+ } else if (value instanceof Double) {
+ syncExtrasBundle.putDouble(key, (Double) value);
+ } else if (value instanceof String) {
+ syncExtrasBundle.putString(key, (String) value);
+ } else if (value == null) {
+ syncExtrasBundle.putString(key, null);
+ } else {
+ Slog.e(TAG, "Unknown extra type.");
+ }
+ }
+ jobInfoExtras.putPersistableBundle("syncExtras", syncExtrasBundle);
+
+ jobInfoExtras.putBoolean("SyncManagerJob", true);
+
+ jobInfoExtras.putString("provider", target.provider);
+ jobInfoExtras.putString("accountName", target.account.name);
+ jobInfoExtras.putString("accountType", target.account.type);
+ jobInfoExtras.putInt("userId", target.userId);
+ jobInfoExtras.putInt("owningUid", owningUid);
+ jobInfoExtras.putString("owningPackage", owningPackage);
+ jobInfoExtras.putInt("reason", reason);
+ jobInfoExtras.putInt("source", syncSource);
+ jobInfoExtras.putBoolean("allowParallelSyncs", allowParallelSyncs);
+ jobInfoExtras.putInt("jobId", jobId);
+ jobInfoExtras.putBoolean("isPeriodic", isPeriodic);
+ jobInfoExtras.putInt("sourcePeriodicId", sourcePeriodicId);
+ jobInfoExtras.putLong("periodMillis", periodMillis);
+ jobInfoExtras.putLong("flexMillis", flexMillis);
+ jobInfoExtras.putLong("expectedRuntime", expectedRuntime);
+ return jobInfoExtras;
+ }
+
+ /**
+ * Reconstructs a sync operation from an extras Bundle. Returns null if the bundle doesn't
+ * contain a valid sync operation.
+ */
+ static SyncOperation maybeCreateFromJobExtras(PersistableBundle jobExtras) {
+ String accountName, accountType;
+ String provider;
+ int userId, owningUid;
+ String owningPackage;
+ int reason, source;
+ int initiatedBy;
+ Bundle extras;
+ boolean allowParallelSyncs, isPeriodic;
+
+ if (!jobExtras.getBoolean("SyncManagerJob", false)) {
+ return null;
+ }
+
+ accountName = jobExtras.getString("accountName");
+ accountType = jobExtras.getString("accountType");
+ provider = jobExtras.getString("provider");
+ userId = jobExtras.getInt("userId", Integer.MAX_VALUE);
+ owningUid = jobExtras.getInt("owningUid");
+ owningPackage = jobExtras.getString("owningPackage");
+ reason = jobExtras.getInt("reason", Integer.MAX_VALUE);
+ source = jobExtras.getInt("source", Integer.MAX_VALUE);
+ allowParallelSyncs = jobExtras.getBoolean("allowParallelSyncs", false);
+ isPeriodic = jobExtras.getBoolean("isPeriodic", false);
+ initiatedBy = jobExtras.getInt("sourcePeriodicId", NO_JOB_ID);
+ extras = new Bundle();
+
+ PersistableBundle syncExtras = jobExtras.getPersistableBundle("syncExtras");
+ if (syncExtras != null) {
+ extras.putAll(syncExtras);
+ }
+
+ for (String key: jobExtras.keySet()) {
+ if (key!= null && key.startsWith("ACCOUNT:")) {
+ String newKey = key.substring(8); // Strip off the 'ACCOUNT:' prefix.
+ PersistableBundle accountsBundle = jobExtras.getPersistableBundle(key);
+ Account account = new Account(accountsBundle.getString("accountName"),
+ accountsBundle.getString("accountType"));
+ extras.putParcelable(newKey, account);
+ }
+ }
+
+ Account account = new Account(accountName, accountType);
+ SyncStorageEngine.EndPoint target =
+ new SyncStorageEngine.EndPoint(account, provider, userId);
+ SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, source,
+ extras, allowParallelSyncs, isPeriodic, initiatedBy);
+ op.jobId = jobExtras.getInt("jobId");
+ op.periodMillis = jobExtras.getLong("periodMillis");
+ op.flexMillis = jobExtras.getLong("flexMillis");
+ op.expectedRuntime = jobExtras.getLong("expectedRuntime");
+ return op;
}
/**
@@ -187,21 +311,46 @@
/**
* Determine whether if this sync operation is running, the provided operation would conflict
* with it.
- * Parallel syncs allow multiple accounts to be synced at the same time.
+ * Parallel syncs allow multiple accounts to be synced at the same time.
*/
- public boolean isConflict(SyncOperation toRun) {
+ boolean isConflict(SyncOperation toRun) {
final SyncStorageEngine.EndPoint other = toRun.target;
- if (target.target_provider) {
- return target.account.type.equals(other.account.type)
- && target.provider.equals(other.provider)
- && target.userId == other.userId
- && (!allowParallelSyncs
- || target.account.name.equals(other.account.name));
- } else {
- // Ops that target a service default to allow parallel syncs, which is handled by the
- // service returning SYNC_IN_PROGRESS if they don't.
- return target.service.equals(other.service) && !allowParallelSyncs;
+ return target.account.type.equals(other.account.type)
+ && target.provider.equals(other.provider)
+ && target.userId == other.userId
+ && (!allowParallelSyncs
+ || target.account.name.equals(other.account.name));
+ }
+
+ boolean isReasonPeriodic() {
+ return reason == REASON_PERIODIC;
+ }
+
+ boolean isDerivedFromFailedPeriodicSync() {
+ return sourcePeriodicId != NO_JOB_ID;
+ }
+
+ int findPriority() {
+ if (isInitialization()) {
+ return 2;
+ } else if (isExpedited()) {
+ return 1;
}
+ return 0;
+ }
+
+ static String toKey(SyncStorageEngine.EndPoint info, Bundle extras) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("provider: ").append(info.provider);
+ sb.append(" account {name=" + info.account.name
+ + ", user="
+ + info.userId
+ + ", type="
+ + info.account.type
+ + "}");
+ sb.append(" extras: ");
+ extrasToStringBuilder(extras, sb);
+ return sb.toString();
}
@Override
@@ -209,10 +358,9 @@
return dump(null, true);
}
- public String dump(PackageManager pm, boolean useOneLine) {
+ String dump(PackageManager pm, boolean useOneLine) {
StringBuilder sb = new StringBuilder();
- if (target.target_provider) {
- sb.append(target.account.name)
+ sb.append(target.account.name)
.append(" u")
.append(target.userId).append(" (")
.append(target.account.type)
@@ -220,21 +368,15 @@
.append(", ")
.append(target.provider)
.append(", ");
- } else if (target.target_service) {
- sb.append(target.service.getPackageName())
- .append(" u")
- .append(target.userId).append(" (")
- .append(target.service.getClassName()).append(")")
- .append(", ");
- }
- sb.append(SyncStorageEngine.SOURCES[syncSource])
- .append(", currentRunTime ")
- .append(effectiveRunTime);
- if (expedited) {
+ sb.append(SyncStorageEngine.SOURCES[syncSource]);
+ if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
sb.append(", EXPEDITED");
}
sb.append(", reason: ");
sb.append(reasonToString(pm, reason));
+ if (isPeriodic) {
+ sb.append(", period: " + periodMillis).append(", flexMillis: " + flexMillis);
+ }
if (!useOneLine) {
sb.append("\n ");
sb.append("owningUid=");
@@ -249,7 +391,7 @@
return sb.toString();
}
- public static String reasonToString(PackageManager pm, int reason) {
+ static String reasonToString(PackageManager pm, int reason) {
if (reason >= 0) {
if (pm != null) {
final String[] packages = pm.getPackagesForUid(reason);
@@ -274,58 +416,30 @@
}
}
- public boolean isInitialization() {
+ boolean isInitialization() {
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
}
- public boolean isExpedited() {
- return expedited;
+ boolean isExpedited() {
+ return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
}
- public boolean ignoreBackoff() {
+ boolean ignoreBackoff() {
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false);
}
- public boolean isNotAllowedOnMetered() {
+ boolean isNotAllowedOnMetered() {
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, false);
}
- public boolean isManual() {
+ boolean isManual() {
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
}
- public boolean isIgnoreSettings() {
+ boolean isIgnoreSettings() {
return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
}
- /** Changed in V3. */
- public static String toKey(SyncStorageEngine.EndPoint info, Bundle extras) {
- StringBuilder sb = new StringBuilder();
- if (info.target_provider) {
- sb.append("provider: ").append(info.provider);
- sb.append(" account {name=" + info.account.name
- + ", user="
- + info.userId
- + ", type="
- + info.account.type
- + "}");
- } else if (info.target_service) {
- sb.append("service {package=" )
- .append(info.service.getPackageName())
- .append(" user=")
- .append(info.userId)
- .append(", class=")
- .append(info.service.getClassName())
- .append("}");
- } else {
- Log.v(TAG, "Converting SyncOperaton to key, invalid target: " + info.toString());
- return "";
- }
- sb.append(" extras: ");
- extrasToStringBuilder(extras, sb);
- return sb.toString();
- }
-
private static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
sb.append("[");
for (String key : bundle.keySet()) {
@@ -334,57 +448,13 @@
sb.append("]");
}
- public String wakeLockName() {
+ String wakeLockName() {
if (wakeLockName != null) {
return wakeLockName;
}
- if (target.target_provider) {
- return (wakeLockName = target.provider
- + "/" + target.account.type
- + "/" + target.account.name);
- } else if (target.target_service) {
- return (wakeLockName = target.service.getPackageName()
- + "/" + target.service.getClassName());
- } else {
- Log.wtf(TAG, "Invalid target getting wakelock name for operation - " + key);
- return null;
- }
- }
-
- /**
- * Update the effective run time of this Operation based on latestRunTime (specified at
- * creation time of sync), delayUntil (specified by SyncAdapter), or backoff (specified by
- * SyncManager on soft failures).
- */
- public void updateEffectiveRunTime() {
- // Regardless of whether we're in backoff or honouring a delayUntil, we still incorporate
- // the flex time provided by the developer.
- effectiveRunTime = ignoreBackoff() ?
- latestRunTime :
- Math.max(Math.max(latestRunTime, delayUntil), backoff);
- }
-
- /**
- * SyncOperations are sorted based on their earliest effective run time.
- * This comparator is used to sort the SyncOps at a given time when
- * deciding which to run, so earliest run time is the best criteria.
- */
- @Override
- public int compareTo(Object o) {
- SyncOperation other = (SyncOperation) o;
- if (expedited != other.expedited) {
- return expedited ? -1 : 1;
- }
- long thisIntervalStart = Math.max(effectiveRunTime - flexTime, 0);
- long otherIntervalStart = Math.max(
- other.effectiveRunTime - other.flexTime, 0);
- if (thisIntervalStart < otherIntervalStart) {
- return -1;
- } else if (otherIntervalStart < thisIntervalStart) {
- return 1;
- } else {
- return 0;
- }
+ return (wakeLockName = target.provider
+ + "/" + target.account.type
+ + "/" + target.account.name);
}
// TODO: Test this to make sure that casting to object doesn't lose the type info for EventLog.
@@ -392,15 +462,8 @@
Object[] logArray = new Object[4];
logArray[1] = event;
logArray[2] = syncSource;
- if (target.target_provider) {
- logArray[0] = target.provider;
- logArray[3] = target.account.name.hashCode();
- } else if (target.target_service) {
- logArray[0] = target.service.getPackageName();
- logArray[3] = target.service.hashCode();
- } else {
- Log.wtf(TAG, "sync op with invalid target: " + key);
- }
+ logArray[0] = target.provider;
+ logArray[3] = target.account.name.hashCode();
return logArray;
}
}
diff --git a/services/core/java/com/android/server/content/SyncQueue.java b/services/core/java/com/android/server/content/SyncQueue.java
deleted file mode 100644
index b15d0d8..0000000
--- a/services/core/java/com/android/server/content/SyncQueue.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2010 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 com.android.server.content;
-
-import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
-import android.content.pm.PackageManager;
-import android.content.SyncAdapterType;
-import android.content.SyncAdaptersCache;
-import android.content.pm.RegisteredServicesCache.ServiceInfo;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.text.format.DateUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import android.util.Slog;
-import com.google.android.collect.Maps;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * Queue of pending sync operations. Not inherently thread safe, external
- * callers are responsible for locking.
- *
- * @hide
- */
-public class SyncQueue {
- private static final String TAG = "SyncManager";
- private final SyncStorageEngine mSyncStorageEngine;
- private final SyncAdaptersCache mSyncAdapters;
- private final PackageManager mPackageManager;
-
- // 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 SyncQueue(PackageManager packageManager, SyncStorageEngine syncStorageEngine,
- final SyncAdaptersCache syncAdapters) {
- mPackageManager = packageManager;
- mSyncStorageEngine = syncStorageEngine;
- mSyncAdapters = syncAdapters;
- }
-
- public void addPendingOperations(int userId) {
- for (SyncStorageEngine.PendingOperation op : mSyncStorageEngine.getPendingOperations()) {
- final SyncStorageEngine.EndPoint info = op.target;
- if (info.userId != userId) continue;
-
- final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
- SyncOperation operationToAdd;
- if (info.target_provider) {
- final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(info.provider, info.account.type), info.userId);
- if (syncAdapterInfo == null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Missing sync adapter info for authority " + op.target);
- }
- continue;
- }
- operationToAdd = new SyncOperation(
- info.account, info.userId, syncAdapterInfo.uid,
- syncAdapterInfo.componentName.getPackageName(), op.reason,
- op.syncSource, info.provider, op.extras,
- op.expedited ? -1 : 0 /* delay */,
- 0 /* flex */,
- backoff != null ? backoff.first : 0L,
- mSyncStorageEngine.getDelayUntilTime(info),
- syncAdapterInfo.type.allowParallelSyncs());
- operationToAdd.pendingOperation = op;
- add(operationToAdd, op);
- } else if (info.target_service) {
- android.content.pm.ServiceInfo sinfo;
- try {
- sinfo = mPackageManager.getServiceInfo(info.service, info.userId);
- } catch (PackageManager.NameNotFoundException e) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.w(TAG, "Missing sync service for authority " + op.target);
- }
- continue;
- }
- if (sinfo == null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.w(TAG, "Missing sync service for authority " + op.target);
- }
- continue;
- }
- operationToAdd = new SyncOperation(
- info.service, info.userId, sinfo.applicationInfo.uid,
- info.service.getPackageName(), op.reason, op.syncSource,
- op.extras,
- op.expedited ? -1 : 0 /* delay */,
- 0 /* flex */,
- backoff != null ? backoff.first : 0,
- mSyncStorageEngine.getDelayUntilTime(info));
- operationToAdd.pendingOperation = op;
- add(operationToAdd, op);
- }
- }
- }
-
- public boolean add(SyncOperation operation) {
- return add(operation, null /* this is not coming from the database */);
- }
-
- /**
- * Adds a SyncOperation to the queue and creates a PendingOperation object to track that sync.
- * If an operation is added that already exists, the existing operation is updated if the newly
- * added operation occurs before (or the interval overlaps).
- */
- private boolean add(SyncOperation operation,
- SyncStorageEngine.PendingOperation pop) {
- // If an operation with the same key exists and this one should run sooner/overlaps,
- // replace the run interval of the existing operation with this new one.
- // Complications: what if the existing operation is expedited but the new operation has an
- // earlier run time? Will not be a problem for periodic syncs (no expedited flag), and for
- // one-off syncs we only change it if the new sync is sooner.
- final String operationKey = operation.key;
- final SyncOperation existingOperation = mOperationsMap.get(operationKey);
-
- if (existingOperation != null) {
- boolean changed = false;
- if (operation.compareTo(existingOperation) <= 0 ) {
- long newRunTime =
- Math.min(existingOperation.latestRunTime, operation.latestRunTime);
- // Take smaller runtime.
- existingOperation.latestRunTime = newRunTime;
- // Take newer flextime.
- existingOperation.flexTime = operation.flexTime;
- changed = true;
- }
- return changed;
- }
-
- operation.pendingOperation = pop;
- // Don't update the PendingOp if one already exists. This really is just a placeholder,
- // no actual scheduling info is placed here.
- if (operation.pendingOperation == null) {
- pop = mSyncStorageEngine.insertIntoPending(operation);
- if (pop == null) {
- throw new IllegalStateException("error adding pending sync operation "
- + operation);
- }
- operation.pendingOperation = pop;
- }
-
- mOperationsMap.put(operationKey, operation);
- return true;
- }
-
- public void removeUserLocked(int userId) {
- ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>();
- for (SyncOperation op : mOperationsMap.values()) {
- if (op.target.userId == userId) {
- opsToRemove.add(op);
- }
- }
- for (SyncOperation op : opsToRemove) {
- remove(op);
- }
- }
-
- public boolean removeUidIfNeededLocked(int uid) {
- ArrayList<SyncOperation> opsToRemove = null;
- for (SyncOperation op : mOperationsMap.values()) {
- if (op.owningUid != uid) {
- continue;
- }
- try {
- if (ActivityManagerNative.getDefault().getAppStartMode(op.owningUid,
- op.owningPackage) == ActivityManager.APP_START_MODE_DISABLED) {
- Slog.w(TAG, "Removing sync " + op.owningUid + ":" + op
- + " -- package not allowed to start");
- continue;
- }
- } catch (RemoteException e) {
- }
- if (opsToRemove == null) {
- opsToRemove = new ArrayList<SyncOperation>();
- }
- opsToRemove.add(op);
- }
- if (opsToRemove == null) {
- return false;
- }
- for (SyncOperation op : opsToRemove) {
- remove(op);
- }
- return true;
- }
-
- /**
- * Remove the specified operation if it is in the queue.
- * @param operation the operation to remove
- */
- public void remove(SyncOperation operation) {
- boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
- SyncOperation operationToRemove = mOperationsMap.remove(operation.key);
- if (isLoggable) {
- Log.v(TAG, "Attempting to remove: " + operation.key);
- }
- if (operationToRemove == null) {
- if (isLoggable) {
- Log.v(TAG, "Could not find: " + operation.key);
- }
- return;
- }
- if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) {
- final String errorMessage = "unable to find pending row for " + operationToRemove;
- Log.e(TAG, errorMessage, new IllegalStateException(errorMessage));
- }
- }
-
- /** Reset backoffs for all operations in the queue. */
- public void clearBackoffs() {
- for (SyncOperation op : mOperationsMap.values()) {
- op.backoff = 0L;
- op.updateEffectiveRunTime();
- }
- }
-
- public void onBackoffChanged(SyncStorageEngine.EndPoint target, long backoff) {
- // For each op that matches the target of the changed op, update its
- // backoff and effectiveStartTime
- for (SyncOperation op : mOperationsMap.values()) {
- if (op.target.matchesSpec(target)) {
- op.backoff = backoff;
- op.updateEffectiveRunTime();
- }
- }
- }
-
- public void onDelayUntilTimeChanged(SyncStorageEngine.EndPoint target, long delayUntil) {
- // for each op that matches the target info of the provided op, change the delay time.
- for (SyncOperation op : mOperationsMap.values()) {
- if (op.target.matchesSpec(target)) {
- op.delayUntil = delayUntil;
- op.updateEffectiveRunTime();
- }
- }
- }
-
- /**
- * Remove all of the SyncOperations associated with a given target.
- *
- * @param info target object provided here can have null Account/provider. This is the case
- * where you want to remove all ops associated with a provider (null Account) or all ops
- * associated with an account (null provider).
- * @param extras option bundle to include to further specify which operation to remove. If this
- * bundle contains sync settings flags, they are ignored.
- */
- public void remove(final SyncStorageEngine.EndPoint info, Bundle extras) {
- Iterator<Map.Entry<String, SyncOperation>> entries = mOperationsMap.entrySet().iterator();
- while (entries.hasNext()) {
- Map.Entry<String, SyncOperation> entry = entries.next();
- SyncOperation syncOperation = entry.getValue();
- final SyncStorageEngine.EndPoint opInfo = syncOperation.target;
- if (!opInfo.matchesSpec(info)) {
- continue;
- }
- if (extras != null
- && !SyncManager.syncExtrasEquals(
- syncOperation.extras,
- extras,
- false /* no config flags*/)) {
- continue;
- }
- entries.remove();
- if (!mSyncStorageEngine.deleteFromPending(syncOperation.pendingOperation)) {
- final String errorMessage = "unable to find pending row for " + syncOperation;
- Log.e(TAG, errorMessage, new IllegalStateException(errorMessage));
- }
- }
- }
-
- public Collection<SyncOperation> getOperations() {
- return mOperationsMap.values();
- }
-
- 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(" ");
- if (operation.effectiveRunTime <= now) {
- sb.append("READY");
- } else {
- sb.append(DateUtils.formatElapsedTime((operation.effectiveRunTime - now) / 1000));
- }
- sb.append(" - ");
- sb.append(operation.dump(mPackageManager, false)).append("\n");
- }
- }
-}
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index c13518b..f8e3e48 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -27,7 +27,6 @@
import android.content.SyncInfo;
import android.content.SyncRequest;
import android.content.SyncStatusInfo;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -40,13 +39,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.AtomicFile;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.ArrayMap;
-import android.util.Xml;
+import android.util.*;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -59,7 +52,6 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
@@ -90,7 +82,7 @@
/** Default time for a periodic sync. */
private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
- /** Percentage of period that is flex by default, if no flex is set. */
+ /** Percentage of period that is flex by default, if no flexMillis is set. */
private static final double DEFAULT_FLEX_PERCENT_SYNC = 0.04;
/** Lower bound on sync time from which we assign a default flex time. */
@@ -105,10 +97,6 @@
/** Enum value for a sync stop event. */
public static final int EVENT_STOP = 1;
- // TODO: i18n -- grab these out of resources.
- /** String names for the sync event types. */
- public static final String[] EVENTS = { "START", "STOP" };
-
/** Enum value for a server-initiated sync. */
public static final int SOURCE_SERVER = 0;
@@ -122,20 +110,17 @@
/** Enum value for a periodic sync. */
public static final int SOURCE_PERIODIC = 4;
-
- /** Enum value for a sync started for a service. */
- public static final int SOURCE_SERVICE = 5;
public static final long NOT_IN_BACKOFF_MODE = -1;
// TODO: i18n -- grab these out of resources.
/** String names for the sync source types. */
public static final String[] SOURCES = { "SERVER",
- "LOCAL",
- "POLL",
- "USER",
- "PERIODIC",
- "SERVICE"};
+ "LOCAL",
+ "POLL",
+ "USER",
+ "PERIODIC",
+ "SERVICE"};
// The MESG column will contain one of these or one of the Error types.
public static final String MESG_SUCCESS = "success";
@@ -155,6 +140,7 @@
private static final int ACCOUNTS_VERSION = 2;
private static HashMap<String, String> sAuthorityRenames;
+ private static PeriodicSyncAddedListener mPeriodicSyncAddedListener;
static {
sAuthorityRenames = new HashMap<String, String>();
@@ -162,58 +148,6 @@
sAuthorityRenames.put("calendar", "com.android.calendar");
}
- public static class PendingOperation {
- final EndPoint target;
- final int reason;
- final int syncSource;
- final Bundle extras; // note: read-only.
- final boolean expedited;
-
- final int authorityId;
- // No longer used.
- // Keep around for sake up updating from pending.bin to pending.xml
- byte[] flatExtras;
-
- PendingOperation(AuthorityInfo authority, int reason, int source,
- Bundle extras, boolean expedited) {
- this.target = authority.target;
- this.syncSource = source;
- this.reason = reason;
- this.extras = extras != null ? new Bundle(extras) : extras;
- this.expedited = expedited;
- this.authorityId = authority.ident;
- }
-
- PendingOperation(PendingOperation other) {
- this.reason = other.reason;
- this.syncSource = other.syncSource;
- this.target = other.target;
- this.extras = other.extras;
- this.authorityId = other.authorityId;
- this.expedited = other.expedited;
- }
-
- /**
- * Considered equal if they target the same sync adapter (A
- * {@link android.content.SyncService}
- * is considered an adapter), for the same userId.
- * @param other PendingOperation to compare.
- * @return true if the two pending ops are the same.
- */
- public boolean equals(PendingOperation other) {
- return target.matchesSpec(other.target);
- }
-
- public String toString() {
- return "service=" + target.service
- + " user=" + target.userId
- + " auth=" + target
- + " account=" + target.account
- + " src=" + syncSource
- + " extras=" + extras;
- }
- }
-
static class AccountInfo {
final AccountAndUser accountAndUser;
final HashMap<String, AuthorityInfo> authorities =
@@ -228,39 +162,21 @@
public static class EndPoint {
public final static EndPoint USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL =
new EndPoint(null, null, UserHandle.USER_ALL);
- final ComponentName service;
- final int serviceUid; // -1 for "any"
final Account account;
final int userId;
final String provider;
- final boolean target_service;
- final boolean target_provider;
-
- public EndPoint(ComponentName service, int userId, int uid) {
- this.service = service;
- this.serviceUid = uid;
- this.userId = userId;
- this.account = null;
- this.provider = null;
- this.target_service = true;
- this.target_provider = false;
- }
public EndPoint(Account account, String provider, int userId) {
this.account = account;
this.provider = provider;
this.userId = userId;
- this.service = null;
- this.serviceUid = -1;
- this.target_service = false;
- this.target_provider = true;
}
/**
* An Endpoint for a sync matches if it targets the same sync adapter for the same user.
*
* @param spec the Endpoint to match. If the spec has null fields, they indicate a wildcard
- * and match any.
+ * and match any.
*/
public boolean matchesSpec(EndPoint spec) {
if (userId != spec.userId
@@ -268,44 +184,26 @@
&& spec.userId != UserHandle.USER_ALL) {
return false;
}
- if (target_service && spec.target_service) {
- if (serviceUid != spec.serviceUid
- && serviceUid >= 0
- && spec.serviceUid >= 0) {
- return false;
- }
- return service.equals(spec.service);
- } else if (target_provider && spec.target_provider) {
- boolean accountsMatch;
- if (spec.account == null) {
- accountsMatch = true;
- } else {
- accountsMatch = account.equals(spec.account);
- }
- boolean providersMatch;
- if (spec.provider == null) {
- providersMatch = true;
- } else {
- providersMatch = provider.equals(spec.provider);
- }
- return accountsMatch && providersMatch;
+ boolean accountsMatch;
+ if (spec.account == null) {
+ accountsMatch = true;
+ } else {
+ accountsMatch = account.equals(spec.account);
}
- return false;
+ boolean providersMatch;
+ if (spec.provider == null) {
+ providersMatch = true;
+ } else {
+ providersMatch = provider.equals(spec.provider);
+ }
+ return accountsMatch && providersMatch;
}
public String toString() {
StringBuilder sb = new StringBuilder();
- if (target_provider) {
- sb.append(account == null ? "ALL ACCS" : account.name)
+ sb.append(account == null ? "ALL ACCS" : account.name)
.append("/")
.append(provider == null ? "ALL PDRS" : provider);
- } else if (target_service) {
- service.appendShortString(sb);
- sb.append(":");
- UserHandle.formatUid(sb,serviceUid);
- } else {
- sb.append("invalid target");
- }
sb.append(":u" + userId);
return sb.toString();
}
@@ -373,12 +271,7 @@
AuthorityInfo(EndPoint info, int id) {
target = info;
ident = id;
- enabled = info.target_provider ?
- SYNC_ENABLED_DEFAULT : true;
- // Service is active by default,
- if (info.target_service) {
- this.syncable = 1;
- }
+ enabled = SYNC_ENABLED_DEFAULT;
periodicSyncs = new ArrayList<PeriodicSync>();
defaultInitialisation();
}
@@ -387,15 +280,11 @@
syncable = NOT_INITIALIZED; // default to "unknown"
backoffTime = -1; // if < 0 then we aren't in backoff mode
backoffDelay = -1; // if < 0 then we aren't in backoff mode
- PeriodicSync defaultSync;
- // Old version is one sync a day.
- if (target.target_provider) {
- defaultSync =
- new PeriodicSync(target.account, target.provider,
- new Bundle(),
- DEFAULT_POLL_FREQUENCY_SECONDS,
- calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS));
- periodicSyncs.add(defaultSync);
+
+ if (mPeriodicSyncAddedListener != null) {
+ mPeriodicSyncAddedListener.onPeriodicSyncAdded(target, new Bundle(),
+ DEFAULT_POLL_FREQUENCY_SECONDS,
+ calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS));
}
}
@@ -439,6 +328,16 @@
public void onSyncRequest(EndPoint info, int reason, Bundle extras);
}
+ interface PeriodicSyncAddedListener {
+ /** Called when a periodic sync is added. */
+ void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency, long flex);
+ }
+
+ interface OnAuthorityRemovedListener {
+ /** Called when an authority is removed. */
+ void onAuthorityRemoved(EndPoint removedAuthority);
+ }
+
// Primary list of all syncable authorities. Also our global lock.
private final SparseArray<AuthorityInfo> mAuthorities =
new SparseArray<AuthorityInfo>();
@@ -446,9 +345,6 @@
private final HashMap<AccountAndUser, AccountInfo> mAccounts
= new HashMap<AccountAndUser, AccountInfo>();
- private final ArrayList<PendingOperation> mPendingOperations =
- new ArrayList<PendingOperation>();
-
private final SparseArray<ArrayList<SyncInfo>> mCurrentSyncs
= new SparseArray<ArrayList<SyncInfo>>();
@@ -499,20 +395,12 @@
*/
private final AtomicFile mStatisticsFile;
- /**
- * This file contains the pending sync operations. It is a binary file,
- * which must be updated every time an operation is added or removed,
- * so we have special handling of it.
- */
- private final AtomicFile mPendingFile;
- private static final int PENDING_FINISH_TO_WRITE = 4;
- private int mNumPendingFinished = 0;
-
private int mNextHistoryId = 0;
private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();
private boolean mDefaultMasterSyncAutomatically;
private OnSyncRequestListener mSyncRequestListener;
+ private OnAuthorityRemovedListener mAuthorityRemovedListener;
private SyncStorageEngine(Context context, File dataDir) {
mContext = context;
@@ -521,7 +409,7 @@
mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically);
+ com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically);
File systemDir = new File(dataDir, "system");
File syncDir = new File(systemDir, "sync");
@@ -531,17 +419,14 @@
mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
- mPendingFile = new AtomicFile(new File(syncDir, "pending.xml"));
mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin"));
readAccountInfoLocked();
readStatusLocked();
- readPendingOperationsLocked();
readStatisticsLocked();
readAndDeleteLegacyAccountInfoLocked();
writeAccountInfoLocked();
writeStatusLocked();
- writePendingOperationsLocked();
writeStatisticsLocked();
}
@@ -572,6 +457,18 @@
}
}
+ protected void setOnAuthorityRemovedListener(OnAuthorityRemovedListener listener) {
+ if (mAuthorityRemovedListener == null) {
+ mAuthorityRemovedListener = listener;
+ }
+ }
+
+ protected void setPeriodicSyncAddedListener(PeriodicSyncAddedListener listener) {
+ if (mPeriodicSyncAddedListener == null) {
+ mPeriodicSyncAddedListener = listener;
+ }
+ }
+
@Override public void handleMessage(Message msg) {
if (msg.what == MSG_WRITE_STATUS) {
synchronized (mAuthorities) {
@@ -622,7 +519,7 @@
}
}
- private void reportChange(int which) {
+ void reportChange(int which) {
ArrayList<ISyncStatusObserver> reports = null;
synchronized (mAuthorities) {
int i = mChangeListeners.beginBroadcast();
@@ -641,7 +538,7 @@
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "reportChange " + which + " to: " + reports);
+ Slog.v(TAG, "reportChange " + which + " to: " + reports);
}
if (reports != null) {
@@ -680,9 +577,9 @@
}
public void setSyncAutomatically(Account account, int userId, String providerName,
- boolean sync) {
+ boolean sync) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName
+ Slog.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName
+ ", user " + userId + " -> " + sync);
}
synchronized (mAuthorities) {
@@ -693,7 +590,7 @@
false);
if (authority.enabled == sync) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
+ Slog.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
}
return;
}
@@ -744,26 +641,6 @@
setSyncableStateForEndPoint(new EndPoint(account, providerName, userId), syncable);
}
- public boolean getIsTargetServiceActive(ComponentName cname, int userId) {
- synchronized (mAuthorities) {
- if (cname != null) {
- AuthorityInfo authority = getAuthorityLocked(
- new EndPoint(cname, userId, -1),
- "get service active");
- if (authority == null) {
- return false;
- }
- return (authority.syncable == 1);
- }
- return false;
- }
- }
-
- public void setIsTargetServiceActive(ComponentName cname, int userId, boolean active) {
- setSyncableStateForEndPoint(new EndPoint(cname, userId, -1), active ?
- AuthorityInfo.SYNCABLE : AuthorityInfo.NOT_SYNCABLE);
- }
-
/**
* An enabled sync service and a syncable provider's adapter both get resolved to the same
* persisted variable - namely the "syncable" attribute for an AuthorityInfo in accounts.xml.
@@ -778,11 +655,11 @@
syncable = AuthorityInfo.NOT_INITIALIZED;
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.d(TAG, "setIsSyncable: " + aInfo.toString() + " -> " + syncable);
+ Slog.d(TAG, "setIsSyncable: " + aInfo.toString() + " -> " + syncable);
}
if (aInfo.syncable == syncable) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing");
+ Slog.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing");
}
return;
}
@@ -811,15 +688,14 @@
*/
public void setBackoff(EndPoint info, long nextSyncTime, long nextDelay) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "setBackoff: " + info
+ Slog.v(TAG, "setBackoff: " + info
+ " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay);
}
boolean changed;
synchronized (mAuthorities) {
- if (info.target_provider
- && (info.account == null || info.provider == null)) {
+ if (info.account == null || info.provider == null) {
// Do more work for a provider sync if the provided info has specified all
- // accounts/providers.
+ // accounts/providers.
changed = setBackoffLocked(
info.account /* may be null */,
info.userId,
@@ -853,7 +729,7 @@
* @return true if a change occured.
*/
private boolean setBackoffLocked(Account account, int userId, String providerName,
- long nextSyncTime, long nextDelay) {
+ long nextSyncTime, long nextDelay) {
boolean changed = false;
for (AccountInfo accountInfo : mAccounts.values()) {
if (account != null && !account.equals(accountInfo.accountAndUser.account)
@@ -876,40 +752,27 @@
return changed;
}
- public void clearAllBackoffsLocked(SyncQueue syncQueue) {
+ public void clearAllBackoffsLocked() {
boolean changed = false;
synchronized (mAuthorities) {
- // Clear backoff for all sync adapters.
- for (AccountInfo accountInfo : mAccounts.values()) {
- for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
- if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE
- || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "clearAllBackoffsLocked:"
- + " authority:" + authorityInfo.target
- + " account:" + accountInfo.accountAndUser.account.name
- + " user:" + accountInfo.accountAndUser.userId
- + " backoffTime was: " + authorityInfo.backoffTime
- + " backoffDelay was: " + authorityInfo.backoffDelay);
- }
- authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
- authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
- changed = true;
+ // Clear backoff for all sync adapters.
+ for (AccountInfo accountInfo : mAccounts.values()) {
+ for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
+ if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE
+ || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, "clearAllBackoffsLocked:"
+ + " authority:" + authorityInfo.target
+ + " account:" + accountInfo.accountAndUser.account.name
+ + " user:" + accountInfo.accountAndUser.userId
+ + " backoffTime was: " + authorityInfo.backoffTime
+ + " backoffDelay was: " + authorityInfo.backoffDelay);
}
+ authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
+ authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
+ changed = true;
}
}
- // Clear backoff for all sync services.
- for (ComponentName service : mServices.keySet()) {
- SparseArray<AuthorityInfo> aInfos = mServices.get(service);
- for (int i = 0; i < aInfos.size(); i++) {
- AuthorityInfo authorityInfo = aInfos.valueAt(i);
- if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE
- || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) {
- authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
- authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
- }
- }
- syncQueue.clearBackoffs();
}
}
@@ -930,7 +793,7 @@
public void setDelayUntilTime(EndPoint info, long delayUntil) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "setDelayUntil: " + info
+ Slog.v(TAG, "setDelayUntil: " + info
+ " -> delayUntil " + delayUntil);
}
synchronized (mAuthorities) {
@@ -943,123 +806,26 @@
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
- public void updateOrAddPeriodicSync(EndPoint info, long period, long flextime, Bundle extras) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "addPeriodicSync: " + info
- + " -> period " + period + ", flex " + flextime + ", extras "
- + extras.toString());
- }
- synchronized (mAuthorities) {
- if (period <= 0) {
- Log.e(TAG, "period < 0, should never happen in updateOrAddPeriodicSync");
- }
- if (extras == null) {
- Log.e(TAG, "null extras, should never happen in updateOrAddPeriodicSync:");
- }
- try {
- PeriodicSync toUpdate;
- if (info.target_provider) {
- toUpdate = new PeriodicSync(info.account,
- info.provider,
- extras,
- period,
- flextime);
- } else {
- return;
- }
- AuthorityInfo authority =
- getOrCreateAuthorityLocked(info, -1, false);
- // add this periodic sync if an equivalent periodic doesn't already exist.
- boolean alreadyPresent = false;
- for (int i = 0, N = authority.periodicSyncs.size(); i < N; i++) {
- PeriodicSync syncInfo = authority.periodicSyncs.get(i);
- if (SyncManager.syncExtrasEquals(syncInfo.extras,
- extras,
- true /* includeSyncSettings*/)) {
- if (period == syncInfo.period &&
- flextime == syncInfo.flexTime) {
- // Absolutely the same.
- return;
- }
- authority.periodicSyncs.set(i, toUpdate);
- alreadyPresent = true;
- break;
- }
- }
- // If we added an entry to the periodicSyncs array also add an entry to
- // the periodic syncs status to correspond to it.
- if (!alreadyPresent) {
- authority.periodicSyncs.add(toUpdate);
- SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
- // A new periodic sync is initialised as already having been run.
- status.setPeriodicSyncTime(
- authority.periodicSyncs.size() - 1,
- System.currentTimeMillis());
- }
- } finally {
- writeAccountInfoLocked();
- writeStatusLocked();
- }
- }
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
- }
-
- public void removePeriodicSync(EndPoint info, Bundle extras) {
- synchronized(mAuthorities) {
- try {
- AuthorityInfo authority =
- getOrCreateAuthorityLocked(info, -1, false);
- // Remove any periodic syncs that match the target and extras.
- SyncStatusInfo status = mSyncStatus.get(authority.ident);
- boolean changed = false;
- Iterator<PeriodicSync> iterator = authority.periodicSyncs.iterator();
- int i = 0;
- while (iterator.hasNext()) {
- PeriodicSync syncInfo = iterator.next();
- if (SyncManager.syncExtrasEquals(syncInfo.extras,
- extras,
- true /* includeSyncSettings */)) {
- iterator.remove();
- changed = true;
- // If we removed an entry from the periodicSyncs array also
- // remove the corresponding entry from the status
- if (status != null) {
- status.removePeriodicSyncTime(i);
- } else {
- Log.e(TAG, "Tried removing sync status on remove periodic sync but"
- + " did not find it.");
- }
- } else {
- i++;
- }
- }
- if (!changed) {
- return;
- }
- } finally {
- writeAccountInfoLocked();
- writeStatusLocked();
- }
- }
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
- }
-
/**
- * @return list of periodic syncs for a target. Never null. If no such syncs exist, returns an
- * empty list.
+ * Restore all periodic syncs read from persisted files. Used to restore periodic syncs
+ * after an OS update.
*/
- public List<PeriodicSync> getPeriodicSyncs(EndPoint info) {
- synchronized (mAuthorities) {
- AuthorityInfo authorityInfo = getAuthorityLocked(info, "getPeriodicSyncs");
- ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
- if (authorityInfo != null) {
- for (PeriodicSync item : authorityInfo.periodicSyncs) {
- // Copy and send out. Necessary for thread-safety although it's parceled.
- syncs.add(new PeriodicSync(item));
- }
- }
- return syncs;
+ boolean restoreAllPeriodicSyncs() {
+ if (mPeriodicSyncAddedListener == null) {
+ return false;
}
+ synchronized (mAuthorities) {
+ for (int i=0; i<mAuthorities.size(); i++) {
+ AuthorityInfo authority = mAuthorities.valueAt(i);
+ for (PeriodicSync periodicSync: authority.periodicSyncs) {
+ mPeriodicSyncAddedListener.onPeriodicSyncAdded(authority.target,
+ periodicSync.extras, periodicSync.period, periodicSync.flexTime);
+ }
+ authority.periodicSyncs.clear();
+ }
+ writeAccountInfoLocked();
+ }
+ return true;
}
public void setMasterSyncAutomatically(boolean flag, int userId) {
@@ -1109,101 +875,18 @@
return false;
}
- public PendingOperation insertIntoPending(SyncOperation op) {
- PendingOperation pop;
+ public void markPending(EndPoint info, boolean pendingValue) {
synchronized (mAuthorities) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "insertIntoPending: authority=" + op.target
- + " extras=" + op.extras);
- }
- final EndPoint info = op.target;
- AuthorityInfo authority =
- getOrCreateAuthorityLocked(info,
- -1 /* desired identifier */,
- true /* write accounts to storage */);
+ AuthorityInfo authority = getOrCreateAuthorityLocked(info,
+ -1 /* desired identifier */,
+ true /* write accounts to storage */);
if (authority == null) {
- return null;
+ return;
}
-
- pop = new PendingOperation(authority, op.reason, op.syncSource, op.extras,
- op.isExpedited());
- mPendingOperations.add(pop);
- appendPendingOperationLocked(pop);
-
SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
- status.pending = true;
+ status.pending = pendingValue;
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
- return pop;
- }
-
- /**
- * Remove from list of pending operations. If successful, search through list for matching
- * authorities. If there are no more pending syncs for the same target,
- * update the SyncStatusInfo for that target.
- * @param op Pending op to delete.
- */
- public boolean deleteFromPending(PendingOperation op) {
- boolean res = false;
- synchronized (mAuthorities) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "deleteFromPending: account=" + op.toString());
- }
- if (mPendingOperations.remove(op)) {
- if (mPendingOperations.size() == 0
- || mNumPendingFinished >= PENDING_FINISH_TO_WRITE) {
- writePendingOperationsLocked();
- mNumPendingFinished = 0;
- } else {
- mNumPendingFinished++;
- }
- AuthorityInfo authority = getAuthorityLocked(op.target, "deleteFromPending");
- if (authority != null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "removing - " + authority.toString());
- }
- final int N = mPendingOperations.size();
- boolean morePending = false;
- for (int i = 0; i < N; i++) {
- PendingOperation cur = mPendingOperations.get(i);
- if (cur.equals(op)) {
- morePending = true;
- break;
- }
- }
-
- if (!morePending) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "no more pending!");
- SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
- status.pending = false;
- }
- }
- res = true;
- }
- }
-
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
- return res;
- }
-
- /**
- * Return a copy of the current array of pending operations. The
- * PendingOperation objects are the real objects stored inside, so that
- * they can be used with deleteFromPending().
- */
- public ArrayList<PendingOperation> getPendingOperations() {
- synchronized (mAuthorities) {
- return new ArrayList<PendingOperation>(mPendingOperations);
- }
- }
-
- /**
- * Return the number of currently pending operations.
- */
- public int getPendingOperationCount() {
- synchronized (mAuthorities) {
- return mPendingOperations.size();
- }
}
/**
@@ -1213,7 +896,7 @@
public void doDatabaseCleanup(Account[] accounts, int userId) {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Updating for new accounts...");
+ Slog.v(TAG, "Updating for new accounts...");
}
SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
Iterator<AccountInfo> accIt = mAccounts.values().iterator();
@@ -1223,7 +906,7 @@
&& acc.accountAndUser.userId == userId) {
// This account no longer exists...
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Account removed: " + acc.accountAndUser);
+ Slog.v(TAG, "Account removed: " + acc.accountAndUser);
}
for (AuthorityInfo auth : acc.authorities.values()) {
removing.put(auth.ident, auth);
@@ -1238,6 +921,10 @@
while (i > 0) {
i--;
int ident = removing.keyAt(i);
+ AuthorityInfo auth = removing.valueAt(i);
+ if (mAuthorityRemovedListener != null) {
+ mAuthorityRemovedListener.onAuthorityRemoved(auth.target);
+ }
mAuthorities.remove(ident);
int j = mSyncStatus.size();
while (j > 0) {
@@ -1256,7 +943,6 @@
}
writeAccountInfoLocked();
writeStatusLocked();
- writePendingOperationsLocked();
writeStatisticsLocked();
}
}
@@ -1270,10 +956,10 @@
final SyncInfo syncInfo;
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "setActiveSync: account="
- + " auth=" + activeSyncContext.mSyncOperation.target
- + " src=" + activeSyncContext.mSyncOperation.syncSource
- + " extras=" + activeSyncContext.mSyncOperation.extras);
+ Slog.v(TAG, "setActiveSync: account="
+ + " auth=" + activeSyncContext.mSyncOperation.target
+ + " src=" + activeSyncContext.mSyncOperation.syncSource
+ + " extras=" + activeSyncContext.mSyncOperation.extras);
}
final EndPoint info = activeSyncContext.mSyncOperation.target;
AuthorityInfo authorityInfo = getOrCreateAuthorityLocked(
@@ -1297,7 +983,7 @@
public void removeActiveSync(SyncInfo syncInfo, int userId) {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "removeActiveSync: account=" + syncInfo.account
+ Slog.v(TAG, "removeActiveSync: account=" + syncInfo.account
+ " user=" + userId
+ " auth=" + syncInfo.authority);
}
@@ -1321,7 +1007,7 @@
long id;
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "insertStartSyncEvent: " + op);
+ Slog.v(TAG, "insertStartSyncEvent: " + op);
}
AuthorityInfo authority = getAuthorityLocked(op.target, "insertStartSyncEvent");
if (authority == null) {
@@ -1342,7 +1028,7 @@
mSyncHistory.remove(mSyncHistory.size()-1);
}
id = item.historyId;
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "returning historyId " + id);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "returning historyId " + id);
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
@@ -1350,10 +1036,10 @@
}
public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage,
- long downstreamActivity, long upstreamActivity) {
+ long downstreamActivity, long upstreamActivity) {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "stopSyncEvent: historyId=" + historyId);
+ Slog.v(TAG, "stopSyncEvent: historyId=" + historyId);
}
SyncHistoryItem item = null;
int i = mSyncHistory.size();
@@ -1367,7 +1053,7 @@
}
if (item == null) {
- Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
+ Slog.w(TAG, "stopSyncEvent: no history for id " + historyId);
return;
}
@@ -1501,22 +1187,6 @@
}
/**
- * Return an array of the current sync status for all authorities. Note
- * that the objects inside the array are the real, live status objects,
- * so be careful what you do with them.
- */
- public ArrayList<SyncStatusInfo> getSyncStatus() {
- synchronized (mAuthorities) {
- final int N = mSyncStatus.size();
- ArrayList<SyncStatusInfo> ops = new ArrayList<SyncStatusInfo>(N);
- for (int i=0; i<N; i++) {
- ops.add(mSyncStatus.valueAt(i));
- }
- return ops;
- }
- }
-
- /**
* Return a copy of the specified target with the corresponding sync status
*/
public Pair<AuthorityInfo, SyncStatusInfo> getCopyOfAuthorityWithSyncStatus(EndPoint info) {
@@ -1529,29 +1199,13 @@
}
/**
- * Return a copy of all authorities with their corresponding sync status
- */
- public ArrayList<Pair<AuthorityInfo, SyncStatusInfo>> getCopyOfAllAuthoritiesWithSyncStatus() {
- synchronized (mAuthorities) {
- ArrayList<Pair<AuthorityInfo, SyncStatusInfo>> infos =
- new ArrayList<Pair<AuthorityInfo, SyncStatusInfo>>(mAuthorities.size());
- for (int i = 0; i < mAuthorities.size(); i++) {
- infos.add(createCopyPairOfAuthorityWithSyncStatusLocked(mAuthorities.valueAt(i)));
- }
- return infos;
- }
- }
-
- /**
* Returns the status that matches the target.
*
* @param info the endpoint target we are querying status info for.
* @return the SyncStatusInfo for the endpoint.
*/
public SyncStatusInfo getStatusByAuthority(EndPoint info) {
- if (info.target_provider && (info.account == null || info.provider == null)) {
- return null;
- } else if (info.target_service && info.service == null) {
+ if (info.account == null || info.provider == null) {
return null;
}
synchronized (mAuthorities) {
@@ -1561,7 +1215,7 @@
AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
if (ainfo != null
&& ainfo.target.matchesSpec(info)) {
- return cur;
+ return cur;
}
}
return null;
@@ -1644,47 +1298,26 @@
* requested target does not exist.
*/
private AuthorityInfo getAuthorityLocked(EndPoint info, String tag) {
- if (info.target_service) {
- SparseArray<AuthorityInfo> aInfo = mServices.get(info.service);
- AuthorityInfo authority = null;
- if (aInfo != null) {
- authority = aInfo.get(info.userId);
- }
- if (authority == null) {
- if (tag != null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, tag + " No authority info found for " + info.service + " for"
- + " user " + info.userId);
- }
+ AccountAndUser au = new AccountAndUser(info.account, info.userId);
+ AccountInfo accountInfo = mAccounts.get(au);
+ if (accountInfo == null) {
+ if (tag != null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, tag + ": unknown account " + au);
}
- return null;
}
- return authority;
- } else if (info.target_provider){
- AccountAndUser au = new AccountAndUser(info.account, info.userId);
- AccountInfo accountInfo = mAccounts.get(au);
- if (accountInfo == null) {
- if (tag != null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, tag + ": unknown account " + au);
- }
- }
- return null;
- }
- AuthorityInfo authority = accountInfo.authorities.get(info.provider);
- if (authority == null) {
- if (tag != null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, tag + ": unknown provider " + info.provider);
- }
- }
- return null;
- }
- return authority;
- } else {
- Log.e(TAG, tag + " Authority : " + info + ", invalid target");
return null;
}
+ AuthorityInfo authority = accountInfo.authorities.get(info.provider);
+ if (authority == null) {
+ if (tag != null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.v(TAG, tag + ": unknown provider " + info.provider);
+ }
+ }
+ return null;
+ }
+ return authority;
}
/**
@@ -1696,29 +1329,16 @@
*/
private AuthorityInfo getOrCreateAuthorityLocked(EndPoint info, int ident, boolean doWrite) {
AuthorityInfo authority = null;
- if (info.target_service) {
- SparseArray<AuthorityInfo> aInfo = mServices.get(info.service);
- if (aInfo == null) {
- aInfo = new SparseArray<AuthorityInfo>();
- mServices.put(info.service, aInfo);
- }
- authority = aInfo.get(info.userId);
- if (authority == null) {
- authority = createAuthorityLocked(info, ident, doWrite);
- aInfo.put(info.userId, authority);
- }
- } else if (info.target_provider) {
- AccountAndUser au = new AccountAndUser(info.account, info.userId);
- AccountInfo account = mAccounts.get(au);
- if (account == null) {
- account = new AccountInfo(au);
- mAccounts.put(au, account);
- }
- authority = account.authorities.get(info.provider);
- if (authority == null) {
- authority = createAuthorityLocked(info, ident, doWrite);
- account.authorities.put(info.provider, authority);
- }
+ AccountAndUser au = new AccountAndUser(info.account, info.userId);
+ AccountInfo account = mAccounts.get(au);
+ if (account == null) {
+ account = new AccountInfo(au);
+ mAccounts.put(au, account);
+ }
+ authority = account.authorities.get(info.provider);
+ if (authority == null) {
+ authority = createAuthorityLocked(info, ident, doWrite);
+ account.authorities.put(info.provider, authority);
}
return authority;
}
@@ -1731,7 +1351,7 @@
doWrite = true;
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "created a new AuthorityInfo for " + info);
+ Slog.v(TAG, "created a new AuthorityInfo for " + info);
}
authority = new AuthorityInfo(info, ident);
mAuthorities.put(ident, authority);
@@ -1743,33 +1363,24 @@
public void removeAuthority(EndPoint info) {
synchronized (mAuthorities) {
- if (info.target_provider) {
- removeAuthorityLocked(info.account, info.userId, info.provider, true /* doWrite */);
- } else {
- SparseArray<AuthorityInfo> aInfos = mServices.get(info.service);
- if (aInfos != null) {
- AuthorityInfo authorityInfo = aInfos.get(info.userId);
- if (authorityInfo != null) {
- mAuthorities.remove(authorityInfo.ident);
- aInfos.delete(info.userId);
- writeAccountInfoLocked();
- }
- }
-
- }
+ removeAuthorityLocked(info.account, info.userId, info.provider, true /* doWrite */);
}
}
+
/**
* Remove an authority associated with a provider. Needs to be a standalone function for
* backward compatibility.
*/
private void removeAuthorityLocked(Account account, int userId, String authorityName,
- boolean doWrite) {
+ boolean doWrite) {
AccountInfo accountInfo = mAccounts.get(new AccountAndUser(account, userId));
if (accountInfo != null) {
final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName);
if (authorityInfo != null) {
+ if (mAuthorityRemovedListener != null) {
+ mAuthorityRemovedListener.onAuthorityRemoved(authorityInfo.target);
+ }
mAuthorities.remove(authorityInfo.ident);
if (doWrite) {
writeAccountInfoLocked();
@@ -1778,30 +1389,6 @@
}
}
- /**
- * Updates (in a synchronized way) the periodic sync time of the specified
- * target id and target periodic sync
- */
- public void setPeriodicSyncTime(int authorityId, PeriodicSync targetPeriodicSync, long when) {
- boolean found = false;
- final AuthorityInfo authorityInfo;
- synchronized (mAuthorities) {
- authorityInfo = mAuthorities.get(authorityId);
- for (int i = 0; i < authorityInfo.periodicSyncs.size(); i++) {
- PeriodicSync periodicSync = authorityInfo.periodicSyncs.get(i);
- if (targetPeriodicSync.equals(periodicSync)) {
- mSyncStatus.get(authorityId).setPeriodicSyncTime(i, when);
- found = true;
- break;
- }
- }
- }
- if (!found) {
- Log.w(TAG, "Ignoring setPeriodicSyncTime request for a sync that does not exist. " +
- "Authority: " + authorityInfo.target);
- }
- }
-
private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
SyncStatusInfo status = mSyncStatus.get(authorityId);
if (status == null) {
@@ -1814,13 +1401,6 @@
public void writeAllState() {
synchronized (mAuthorities) {
// Account info is always written so no need to do it here.
-
- if (mNumPendingFinished > 0) {
- // Only write these if they are out of date.
- writePendingOperationsLocked();
- }
-
- // Just always write these... they are likely out of date.
writeStatusLocked();
writeStatisticsLocked();
}
@@ -1834,18 +1414,15 @@
mAuthorities.clear();
mAccounts.clear();
mServices.clear();
- mPendingOperations.clear();
mSyncStatus.clear();
mSyncHistory.clear();
readAccountInfoLocked();
readStatusLocked();
- readPendingOperationsLocked();
readStatisticsLocked();
readAndDeleteLegacyAccountInfoLocked();
writeAccountInfoLocked();
writeStatusLocked();
- writePendingOperationsLocked();
writeStatisticsLocked();
}
}
@@ -1859,7 +1436,7 @@
try {
fis = mAccountInfoFile.openRead();
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Reading " + mAccountInfoFile.getBaseFile());
+ Slog.v(TAG_FILE, "Reading " + mAccountInfoFile.getBaseFile());
}
XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, StandardCharsets.UTF_8.name());
@@ -1869,7 +1446,7 @@
eventType = parser.next();
}
if (eventType == XmlPullParser.END_DOCUMENT) {
- Log.i(TAG, "No initial accounts");
+ Slog.i(TAG, "No initial accounts");
return;
}
@@ -1933,11 +1510,11 @@
} while (eventType != XmlPullParser.END_DOCUMENT);
}
} catch (XmlPullParserException e) {
- Log.w(TAG, "Error reading accounts", e);
+ Slog.w(TAG, "Error reading accounts", e);
return;
} catch (java.io.IOException e) {
- if (fis == null) Log.i(TAG, "No initial accounts");
- else Log.w(TAG, "Error reading accounts", e);
+ if (fis == null) Slog.i(TAG, "No initial accounts");
+ else Slog.w(TAG, "Error reading accounts", e);
return;
} finally {
mNextAuthorityId = Math.max(highestAuthorityId + 1, mNextAuthorityId);
@@ -1977,10 +1554,6 @@
final int N = mAuthorities.size();
for (int i = 0; i < N; i++) {
AuthorityInfo authority = mAuthorities.valueAt(i);
- // skip this authority if it doesn't target a provider
- if (authority.target.target_service) {
- continue;
- }
// skip this authority if it isn't one of the renamed ones
final String newAuthorityName = sAuthorityRenames.get(authority.target.provider);
if (newAuthorityName == null) {
@@ -2030,9 +1603,9 @@
try {
userId = Integer.parseInt(user);
} catch (NumberFormatException e) {
- Log.e(TAG, "error parsing the user for listen-for-tickles", e);
+ Slog.e(TAG, "error parsing the user for listen-for-tickles", e);
} catch (NullPointerException e) {
- Log.e(TAG, "the user in listen-for-tickles is null", e);
+ Slog.e(TAG, "the user in listen-for-tickles is null", e);
}
String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
boolean listen = enabled == null || Boolean.parseBoolean(enabled);
@@ -2045,9 +1618,9 @@
try {
id = Integer.parseInt(parser.getAttributeValue(null, "id"));
} catch (NumberFormatException e) {
- Log.e(TAG, "error parsing the id of the authority", e);
+ Slog.e(TAG, "error parsing the id of the authority", e);
} catch (NullPointerException e) {
- Log.e(TAG, "the id of the authority is null", e);
+ Slog.e(TAG, "the id of the authority is null", e);
}
if (id >= 0) {
String authorityName = parser.getAttributeValue(null, "authority");
@@ -2065,7 +1638,7 @@
}
authority = mAuthorities.get(id);
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Adding authority:"
+ Slog.v(TAG_FILE, "Adding authority:"
+ " account=" + accountName
+ " accountType=" + accountType
+ " auth=" + authorityName
@@ -2077,27 +1650,13 @@
}
if (authority == null) {
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Creating authority entry");
+ Slog.v(TAG_FILE, "Creating authority entry");
}
- EndPoint info;
+ EndPoint info = null;
if (accountName != null && authorityName != null) {
info = new EndPoint(
new Account(accountName, accountType),
authorityName, userId);
- } else {
- final ComponentName cname = new ComponentName(packageName, className);
- android.content.pm.ServiceInfo sinfo = null;
- try {
- sinfo = mContext.getPackageManager().getServiceInfo(cname, userId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.w(TAG, "Not restoring sync " + cname
- + " -- can't find service for user " + userId);
- }
- if (sinfo != null) {
- info = new EndPoint(cname, userId, sinfo.applicationInfo.uid);
- } else {
- info = null;
- }
}
if (info != null) {
authority = getOrCreateAuthorityLocked(info, id, false);
@@ -2128,7 +1687,7 @@
}
} else {
- Log.w(TAG, "Failure adding authority: account="
+ Slog.w(TAG, "Failure adding authority: account="
+ accountName + " auth=" + authorityName
+ " enabled=" + enabled
+ " syncable=" + syncable);
@@ -2149,35 +1708,30 @@
try {
period = Long.parseLong(periodValue);
} catch (NumberFormatException e) {
- Log.e(TAG, "error parsing the period of a periodic sync", e);
+ Slog.e(TAG, "error parsing the period of a periodic sync", e);
return null;
} catch (NullPointerException e) {
- Log.e(TAG, "the period of a periodic sync is null", e);
+ Slog.e(TAG, "the period of a periodic sync is null", e);
return null;
}
try {
flextime = Long.parseLong(flexValue);
} catch (NumberFormatException e) {
flextime = calculateDefaultFlexTime(period);
- Log.e(TAG, "Error formatting value parsed for periodic sync flex: " + flexValue
+ Slog.e(TAG, "Error formatting value parsed for periodic sync flex: " + flexValue
+ ", using default: "
+ flextime);
} catch (NullPointerException expected) {
flextime = calculateDefaultFlexTime(period);
- Log.d(TAG, "No flex time specified for this sync, using a default. period: "
- + period + " flex: " + flextime);
+ Slog.d(TAG, "No flex time specified for this sync, using a default. period: "
+ + period + " flex: " + flextime);
}
PeriodicSync periodicSync;
- if (authorityInfo.target.target_provider) {
- periodicSync =
+ periodicSync =
new PeriodicSync(authorityInfo.target.account,
authorityInfo.target.provider,
extras,
period, flextime);
- } else {
- Log.e(TAG, "Unknown target.");
- return null;
- }
authorityInfo.periodicSyncs.add(periodicSync);
return periodicSync;
}
@@ -2205,9 +1759,9 @@
extras.putParcelable(name, new Account(value1, value2));
}
} catch (NumberFormatException e) {
- Log.e(TAG, "error parsing bundle value", e);
+ Slog.e(TAG, "error parsing bundle value", e);
} catch (NullPointerException e) {
- Log.e(TAG, "error parsing bundle value", e);
+ Slog.e(TAG, "error parsing bundle value", e);
}
}
@@ -2216,7 +1770,7 @@
*/
private void writeAccountInfoLocked() {
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Writing new " + mAccountInfoFile.getBaseFile());
+ Slog.v(TAG_FILE, "Writing new " + mAccountInfoFile.getBaseFile());
}
FileOutputStream fos = null;
@@ -2251,30 +1805,17 @@
out.attribute(null, "id", Integer.toString(authority.ident));
out.attribute(null, XML_ATTR_USER, Integer.toString(info.userId));
out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled));
- if (info.service == null) {
- out.attribute(null, "account", info.account.name);
- out.attribute(null, "type", info.account.type);
- out.attribute(null, "authority", info.provider);
- } else {
- out.attribute(null, "package", info.service.getPackageName());
- out.attribute(null, "class", info.service.getClassName());
- }
+ out.attribute(null, "account", info.account.name);
+ out.attribute(null, "type", info.account.type);
+ out.attribute(null, "authority", info.provider);
out.attribute(null, "syncable", Integer.toString(authority.syncable));
- for (PeriodicSync periodicSync : authority.periodicSyncs) {
- out.startTag(null, "periodicSync");
- out.attribute(null, "period", Long.toString(periodicSync.period));
- out.attribute(null, "flex", Long.toString(periodicSync.flexTime));
- final Bundle extras = periodicSync.extras;
- extrasToXml(out, extras);
- out.endTag(null, "periodicSync");
- }
out.endTag(null, "authority");
}
out.endTag(null, "accounts");
out.endDocument();
mAccountInfoFile.finishWrite(fos);
} catch (java.io.IOException e1) {
- Log.w(TAG, "Error writing accounts", e1);
+ Slog.w(TAG, "Error writing accounts", e1);
if (fos != null) {
mAccountInfoFile.failWrite(fos);
}
@@ -2313,7 +1854,7 @@
// Copy in all of the status information, as well as accounts.
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Reading legacy sync accounts db");
+ Slog.v(TAG_FILE, "Reading legacy sync accounts db");
}
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables("stats, status");
@@ -2429,7 +1970,7 @@
*/
private void readStatusLocked() {
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Reading " + mStatusFile.getBaseFile());
+ Slog.v(TAG_FILE, "Reading " + mStatusFile.getBaseFile());
}
try {
byte[] data = mStatusFile.readFully();
@@ -2443,18 +1984,18 @@
if (mAuthorities.indexOfKey(status.authorityId) >= 0) {
status.pending = false;
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Adding status for id " + status.authorityId);
+ Slog.v(TAG_FILE, "Adding status for id " + status.authorityId);
}
mSyncStatus.put(status.authorityId, status);
}
} else {
// Ooops.
- Log.w(TAG, "Unknown status token: " + token);
+ Slog.w(TAG, "Unknown status token: " + token);
break;
}
}
} catch (java.io.IOException e) {
- Log.i(TAG, "No initial status");
+ Slog.i(TAG, "No initial status");
}
}
@@ -2463,7 +2004,7 @@
*/
private void writeStatusLocked() {
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Writing new " + mStatusFile.getBaseFile());
+ Slog.v(TAG_FILE, "Writing new " + mStatusFile.getBaseFile());
}
// The file is being written, so we don't need to have a scheduled
@@ -2486,266 +2027,13 @@
mStatusFile.finishWrite(fos);
} catch (java.io.IOException e1) {
- Log.w(TAG, "Error writing status", e1);
+ Slog.w(TAG, "Error writing status", e1);
if (fos != null) {
mStatusFile.failWrite(fos);
}
}
}
- public static final int PENDING_OPERATION_VERSION = 3;
-
- /** Read all pending operations back in to the initial engine state. */
- private void readPendingOperationsLocked() {
- FileInputStream fis = null;
- if (!mPendingFile.getBaseFile().exists()) {
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "No pending operation file.");
- }
- return;
- }
- try {
- fis = mPendingFile.openRead();
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Reading " + mPendingFile.getBaseFile());
- }
- XmlPullParser parser;
- parser = Xml.newPullParser();
- parser.setInput(fis, StandardCharsets.UTF_8.name());
-
- int eventType = parser.getEventType();
- while (eventType != XmlPullParser.START_TAG &&
- eventType != XmlPullParser.END_DOCUMENT) {
- eventType = parser.next();
- }
- if (eventType == XmlPullParser.END_DOCUMENT) return; // Nothing to read.
-
- do {
- PendingOperation pop = null;
- if (eventType == XmlPullParser.START_TAG) {
- try {
- String tagName = parser.getName();
- if (parser.getDepth() == 1 && "op".equals(tagName)) {
- // Verify version.
- String versionString =
- parser.getAttributeValue(null, XML_ATTR_VERSION);
- if (versionString == null ||
- Integer.parseInt(versionString) != PENDING_OPERATION_VERSION) {
- Log.w(TAG, "Unknown pending operation version " + versionString);
- throw new java.io.IOException("Unknown version.");
- }
- int authorityId = Integer.valueOf(parser.getAttributeValue(
- null, XML_ATTR_AUTHORITYID));
- boolean expedited = Boolean.valueOf(parser.getAttributeValue(
- null, XML_ATTR_EXPEDITED));
- int syncSource = Integer.valueOf(parser.getAttributeValue(
- null, XML_ATTR_SOURCE));
- int reason = Integer.valueOf(parser.getAttributeValue(
- null, XML_ATTR_REASON));
- AuthorityInfo authority = mAuthorities.get(authorityId);
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, authorityId + " " + expedited + " " + syncSource + " "
- + reason);
- }
- if (authority != null) {
- pop = new PendingOperation(
- authority, reason, syncSource, new Bundle(), expedited);
- pop.flatExtras = null; // No longer used.
- mPendingOperations.add(pop);
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "Adding pending op: "
- + pop.target
- + " src=" + pop.syncSource
- + " reason=" + pop.reason
- + " expedited=" + pop.expedited);
- }
- } else {
- // Skip non-existent authority.
- pop = null;
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG_FILE, "No authority found for " + authorityId
- + ", skipping");
- }
- }
- } else if (parser.getDepth() == 2 &&
- pop != null &&
- "extra".equals(tagName)) {
- parseExtra(parser, pop.extras);
- }
- } catch (NumberFormatException e) {
- Log.d(TAG, "Invalid data in xml file.", e);
- }
- }
- eventType = parser.next();
- } while(eventType != XmlPullParser.END_DOCUMENT);
- } catch (java.io.IOException e) {
- Log.w(TAG_FILE, "Error reading pending data.", e);
- } catch (XmlPullParserException e) {
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.w(TAG_FILE, "Error parsing pending ops xml.", e);
- }
- } finally {
- if (fis != null) {
- try {
- fis.close();
- } catch (java.io.IOException e1) {}
- }
- }
- }
-
- static private byte[] flattenBundle(Bundle bundle) {
- byte[] flatData = null;
- Parcel parcel = Parcel.obtain();
- try {
- bundle.writeToParcel(parcel, 0);
- flatData = parcel.marshall();
- } finally {
- parcel.recycle();
- }
- return flatData;
- }
-
- static private Bundle unflattenBundle(byte[] flatData) {
- Bundle bundle;
- Parcel parcel = Parcel.obtain();
- try {
- parcel.unmarshall(flatData, 0, flatData.length);
- parcel.setDataPosition(0);
- bundle = parcel.readBundle();
- } catch (RuntimeException e) {
- // A RuntimeException is thrown if we were unable to parse the parcel.
- // Create an empty parcel in this case.
- bundle = new Bundle();
- } finally {
- parcel.recycle();
- }
- return bundle;
- }
-
- private static final String XML_ATTR_VERSION = "version";
- private static final String XML_ATTR_AUTHORITYID = "authority_id";
- private static final String XML_ATTR_SOURCE = "source";
- private static final String XML_ATTR_EXPEDITED = "expedited";
- private static final String XML_ATTR_REASON = "reason";
-
- /**
- * Write all currently pending ops to the pending ops file.
- */
- private void writePendingOperationsLocked() {
- final int N = mPendingOperations.size();
- FileOutputStream fos = null;
- try {
- if (N == 0) {
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)){
- Log.v(TAG, "Truncating " + mPendingFile.getBaseFile());
- }
- mPendingFile.truncate();
- return;
- }
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
- }
- fos = mPendingFile.startWrite();
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
-
- for (int i = 0; i < N; i++) {
- PendingOperation pop = mPendingOperations.get(i);
- writePendingOperationLocked(pop, out);
- }
- out.endDocument();
- mPendingFile.finishWrite(fos);
- } catch (java.io.IOException e1) {
- Log.w(TAG, "Error writing pending operations", e1);
- if (fos != null) {
- mPendingFile.failWrite(fos);
- }
- }
- }
-
- /** Write all currently pending ops to the pending ops file. */
- private void writePendingOperationLocked(PendingOperation pop, XmlSerializer out)
- throws IOException {
- // Pending operation.
- out.startTag(null, "op");
-
- out.attribute(null, XML_ATTR_VERSION, Integer.toString(PENDING_OPERATION_VERSION));
- out.attribute(null, XML_ATTR_AUTHORITYID, Integer.toString(pop.authorityId));
- out.attribute(null, XML_ATTR_SOURCE, Integer.toString(pop.syncSource));
- out.attribute(null, XML_ATTR_EXPEDITED, Boolean.toString(pop.expedited));
- out.attribute(null, XML_ATTR_REASON, Integer.toString(pop.reason));
- extrasToXml(out, pop.extras);
-
- out.endTag(null, "op");
- }
-
- /**
- * Append the given operation to the pending ops file; if unable to,
- * write all pending ops.
- */
- private void appendPendingOperationLocked(PendingOperation op) {
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Appending to " + mPendingFile.getBaseFile());
- }
- FileOutputStream fos = null;
- try {
- fos = mPendingFile.openAppend();
- } catch (java.io.IOException e) {
- if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Failed append; writing full file");
- }
- writePendingOperationsLocked();
- return;
- }
-
- try {
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(fos, StandardCharsets.UTF_8.name());
- writePendingOperationLocked(op, out);
- out.endDocument();
- mPendingFile.finishWrite(fos);
- } catch (java.io.IOException e1) {
- Log.w(TAG, "Error writing appending operation", e1);
- mPendingFile.failWrite(fos);
- } finally {
- try {
- fos.close();
- } catch (IOException e) {}
- }
- }
-
- private void extrasToXml(XmlSerializer out, Bundle extras) throws java.io.IOException {
- for (String key : extras.keySet()) {
- out.startTag(null, "extra");
- out.attribute(null, "name", key);
- final Object value = extras.get(key);
- if (value instanceof Long) {
- out.attribute(null, "type", "long");
- out.attribute(null, "value1", value.toString());
- } else if (value instanceof Integer) {
- out.attribute(null, "type", "integer");
- out.attribute(null, "value1", value.toString());
- } else if (value instanceof Boolean) {
- out.attribute(null, "type", "boolean");
- out.attribute(null, "value1", value.toString());
- } else if (value instanceof Float) {
- out.attribute(null, "type", "float");
- out.attribute(null, "value1", value.toString());
- } else if (value instanceof Double) {
- out.attribute(null, "type", "double");
- out.attribute(null, "value1", value.toString());
- } else if (value instanceof String) {
- out.attribute(null, "type", "string");
- out.attribute(null, "value1", value.toString());
- } else if (value instanceof Account) {
- out.attribute(null, "type", "account");
- out.attribute(null, "value1", ((Account)value).name);
- out.attribute(null, "value2", ((Account)value).type);
- }
- out.endTag(null, "extra");
- }
- }
-
private void requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras) {
if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
&& mSyncRequestListener != null) {
@@ -2755,20 +2043,13 @@
new SyncRequest.Builder()
.syncOnce()
.setExtras(extras);
- if (authorityInfo.target.target_provider) {
- req.setSyncAdapter(authorityInfo.target.account, authorityInfo.target.provider);
- } else {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Unknown target, skipping sync request.");
- }
- return;
- }
+ req.setSyncAdapter(authorityInfo.target.account, authorityInfo.target.provider);
ContentResolver.requestSync(req.build());
}
}
private void requestSync(Account account, int userId, int reason, String authority,
- Bundle extras) {
+ Bundle extras) {
// If this is happening in the system process, then call the syncrequest listener
// to make a request back to the SyncManager directly.
// If this is probably a test instance, then call back through the ContentResolver
@@ -2776,9 +2057,9 @@
if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
&& mSyncRequestListener != null) {
mSyncRequestListener.onSyncRequest(
- new EndPoint(account, authority, userId),
- reason,
- extras);
+ new EndPoint(account, authority, userId),
+ reason,
+ extras);
} else {
ContentResolver.requestSync(account, authority, extras);
}
@@ -2817,12 +2098,12 @@
}
} else {
// Ooops.
- Log.w(TAG, "Unknown stats token: " + token);
+ Slog.w(TAG, "Unknown stats token: " + token);
break;
}
}
} catch (java.io.IOException e) {
- Log.i(TAG, "No initial statistics");
+ Slog.i(TAG, "No initial statistics");
}
}
@@ -2831,7 +2112,7 @@
*/
private void writeStatisticsLocked() {
if (Log.isLoggable(TAG_FILE, Log.VERBOSE)) {
- Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
+ Slog.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
}
// The file is being written, so we don't need to have a scheduled
@@ -2861,7 +2142,7 @@
mStatisticsFile.finishWrite(fos);
} catch (java.io.IOException e1) {
- Log.w(TAG, "Error writing stats", e1);
+ Slog.w(TAG, "Error writing stats", e1);
if (fos != null) {
mStatisticsFile.failWrite(fos);
}
@@ -2869,18 +2150,6 @@
}
/**
- * Dump state of PendingOperations.
- */
- public void dumpPendingOperations(StringBuilder sb) {
- sb.append("Pending Ops: ").append(mPendingOperations.size()).append(" operation(s)\n");
- for (PendingOperation pop : mPendingOperations) {
- sb.append("(info: " + pop.target.toString())
- .append(", extras: " + pop.extras)
- .append(")\n");
- }
- }
-
- /**
* Let the BackupManager know that account sync settings have changed. This will trigger
* {@link com.android.server.backup.SystemBackupAgent} to run.
*/
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 43cd44f..4c269989 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -83,7 +83,7 @@
public static final boolean DEBUG = false;
/** The number of concurrent jobs we run at one time. */
private static final int MAX_JOB_CONTEXTS_COUNT
- = ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
+ = ActivityManager.isLowRamDeviceStatic() ? 3 : 6;
static final String TAG = "JobSchedulerService";
/** Master list of jobs. */
final JobStore mJobs;
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index c174a92..313dc8b 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -20,6 +20,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.os.SystemClock;
import android.provider.Settings;
import android.test.AndroidTestCase;
@@ -65,10 +66,6 @@
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
- 100, /* run time from now*/
- 10, /* flex */
- 1000,
- 10000,
false);
// Same as op1 but different time infos
@@ -77,10 +74,6 @@
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
- 200,
- 20,
- 2000,
- 20000,
false);
// Same as op1 but different authority
@@ -89,10 +82,6 @@
SyncOperation.REASON_PERIODIC,
"authority2",
b1,
- 100,
- 10,
- 1000,
- 10000,
false);
// Same as op1 but different account
@@ -101,10 +90,6 @@
SyncOperation.REASON_PERIODIC,
"authority1",
b1,
- 100,
- 10,
- 1000,
- 10000,
false);
// Same as op1 but different bundle
@@ -113,10 +98,6 @@
SyncOperation.REASON_PERIODIC,
"authority1",
b2,
- 100,
- 10,
- 1000,
- 10000,
false);
assertEquals(op1.key, op2.key);
@@ -126,66 +107,52 @@
}
@SmallTest
- public void testCompareTo() {
- long soon = 1000;
- long soonFlex = 50;
- long after = 1500;
- long afterFlex = 100;
- SyncOperation op1 = new SyncOperation(mDummy, 0, 0, "foo", 0, SyncOperation.REASON_PERIODIC,
- "authority1", mEmpty, soon, soonFlex, mUnimportantLong, mUnimportantLong, true);
+ public void testConversionToExtras() {
+ Account account1 = new Account("account1", "type1");
+ Bundle b1 = new Bundle();
+ b1.putParcelable("acc", account1);
+ b1.putString("str", "String");
- // Interval disjoint from and after op1.
- SyncOperation op2 = new SyncOperation(mDummy, 0, 0, "foo", 0, SyncOperation.REASON_PERIODIC,
- "authority1", mEmpty, after, afterFlex, mUnimportantLong, mUnimportantLong, true);
+ SyncOperation op1 = new SyncOperation(account1, 0,
+ 1, "foo", 0,
+ SyncOperation.REASON_PERIODIC,
+ "authority1",
+ b1,
+ false);
- // Interval equivalent to op1, but expedited.
- Bundle b2 = new Bundle();
- b2.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- SyncOperation op3 = new SyncOperation(mDummy, 0, 0, "foo", 0, 0,
- "authority1", b2, -1, soonFlex, mUnimportantLong, mUnimportantLong, true);
+ PersistableBundle pb = op1.toJobInfoExtras();
+ SyncOperation op2 = SyncOperation.maybeCreateFromJobExtras(pb);
- // Interval overlaps but not equivalent to op1.
- SyncOperation op4 = new SyncOperation(mDummy, 0, 0, "foo", 0, SyncOperation.REASON_PERIODIC,
- "authority1", mEmpty, soon + 100, soonFlex + 100, mUnimportantLong, mUnimportantLong, true);
-
- assertTrue(op1.compareTo(op2) == -1);
- assertTrue("less than not transitive.", op2.compareTo(op1) == 1);
- assertTrue("Expedited sync not smaller than non-expedited.", op1.compareTo(op3) == 1);
- assertTrue("greater than not transitive. ", op3.compareTo(op1) == -1);
- assertTrue("overlapping intervals not correctly compared.", op1.compareTo(op4) == -1);
- assertTrue("equality not transitive.", op4.compareTo(op1) == 1);
+ assertTrue("Account fields in extras not persisted.",
+ account1.equals(op2.extras.get("acc")));
+ assertTrue("Fields in extras not persisted", "String".equals(op2.extras.getString("str")));
}
@SmallTest
- public void testCopyConstructor() {
- long fiveSecondsFromNow = 5 * 1000L;
- long twoSecondsFlex = 2 * 1000L;
- long eightSeconds = 8 * 1000L;
- long fourSeconds = 4 * 1000L;
+ public void testConversionFromExtras() {
+ PersistableBundle extras = new PersistableBundle();
+ SyncOperation op = SyncOperation.maybeCreateFromJobExtras(extras);
+ assertTrue("Non sync operation bundle falsely converted to SyncOperation.", op == null);
+ }
- Bundle withExpedited = new Bundle();
- withExpedited.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- SyncOperation op = new SyncOperation(mDummy, 0, 0, "foo", 0,
- SyncOperation.REASON_USER_START,
- mAuthority, withExpedited, fiveSecondsFromNow, twoSecondsFlex,
- eightSeconds /* backoff */, fourSeconds /* delayUntil */, true);
- // Create another sync op to be rerun in 5 minutes.
- long now = SystemClock.elapsedRealtime();
- SyncOperation copy = new SyncOperation(op, fiveSecondsFromNow * 60);
- // Copying an expedited sync to be re-run should not keep expedited property.
- assertFalse("A rescheduled sync based off an expedited should not be expedited!",
- copy.isExpedited());
- assertFalse("A rescheduled sync based off an expedited should not have expedited=true in"
- + "its bundle.",
- copy.extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false));
- assertTrue("Copied sync is not respecting new provided run-time.",
- copy.latestRunTime == (now + fiveSecondsFromNow * 60));
- assertTrue("A rescheduled sync should not have any flex.",
- copy.flexTime == 0L);
- assertTrue("A rescheduled op should honour the old op's backoff.",
- copy.backoff == eightSeconds);
- assertTrue("A rescheduled op should honour the old op's delayUntil param.",
- copy.delayUntil == fourSeconds);
-
+ /**
+ * Tests whether a failed periodic sync operation is converted correctly into a one time
+ * sync operation, and whether the periodic sync can be re-created from the one-time operation.
+ */
+ @SmallTest
+ public void testFailedPeriodicConversion() {
+ SyncStorageEngine.EndPoint ep = new SyncStorageEngine.EndPoint(new Account("name", "type"),
+ "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 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);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
index b22eb53..91c0de6 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
@@ -98,288 +98,18 @@
SyncOperation.REASON_PERIODIC,
SyncStorageEngine.SOURCE_LOCAL,
authority,
- Bundle.EMPTY, time0, 0 /* flex*/, 0, 0, true);
+ Bundle.EMPTY, true);
long historyId = engine.insertStartSyncEvent(op, time0);
long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
}
- /**
- * Test persistence of pending operations.
- */
- @MediumTest
- public void testAppendPending() throws Exception {
- SyncOperation sop = new SyncOperation(account1,
- DEFAULT_USER, 0, "foo",
- SyncOperation.REASON_PERIODIC,
- SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
- 0 /* runtime */, 0 /* flex */, 0 /* backoff */, 0 /* delayuntil */,
- true /* expedited */);
- engine.insertIntoPending(sop);
-
- // Force engine to read from disk.
- engine.clearAndReadState();
-
- assertTrue(engine.getPendingOperationCount() == 1);
- List<SyncStorageEngine.PendingOperation> pops = engine.getPendingOperations();
- SyncStorageEngine.PendingOperation popRetrieved = pops.get(0);
- assertEquals(sop.target.account, popRetrieved.target.account);
- assertEquals(sop.target.provider, popRetrieved.target.provider);
- assertEquals(sop.target.service, popRetrieved.target.service);
- assertEquals(sop.target.userId, popRetrieved.target.userId);
- assertEquals(sop.reason, popRetrieved.reason);
- assertEquals(sop.syncSource, popRetrieved.syncSource);
- assertEquals(sop.isExpedited(), popRetrieved.expedited);
- assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
- }
-
- /**
- * Verify {@link com.android.server.content.SyncStorageEngine#writePendingOperationsLocked()}
- */
- public void testWritePendingOperationsLocked() throws Exception {
- SyncOperation sop = new SyncOperation(account1,
- DEFAULT_USER, 0, "foo",
- SyncOperation.REASON_IS_SYNCABLE,
- SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
- 1000L /* runtime */, 57L /* flex */, 0 /* backoff */, 0 /* delayuntil */,
- true /* expedited */);
- SyncOperation sop1 = new SyncOperation(account2,
- DEFAULT_USER, 0, "foo",
- SyncOperation.REASON_PERIODIC,
- SyncStorageEngine.SOURCE_LOCAL, authority1, defaultBundle,
- 0 /* runtime */, 0 /* flex */, 20L /* backoff */, 100L /* delayuntil */,
- false /* expedited */);
- SyncOperation deleted = new SyncOperation(account2,
- DEFAULT_USER, 0, "foo",
- SyncOperation.REASON_SYNC_AUTO,
- SyncStorageEngine.SOURCE_LOCAL, authority1, Bundle.EMPTY,
- 0 /* runtime */, 0 /* flex */, 20L /* backoff */, 100L /* delayuntil */,
- false /* expedited */);
- engine.insertIntoPending(sop);
- engine.insertIntoPending(sop1);
- engine.insertIntoPending(deleted);
-
- SyncStorageEngine.PendingOperation popDeleted = engine.getPendingOperations().get(2);
- // Free verifying, going to delete it anyway.
- assertEquals(deleted.target.account, popDeleted.target.account);
- assertEquals(deleted.target.provider, popDeleted.target.provider);
- assertEquals(deleted.target.service, popDeleted.target.service);
- assertEquals(deleted.target.userId, popDeleted.target.userId);
- assertEquals(deleted.reason, popDeleted.reason);
- assertEquals(deleted.syncSource, popDeleted.syncSource);
- assertEquals(deleted.isExpedited(), popDeleted.expedited);
- assert(android.content.PeriodicSync.syncExtrasEquals(deleted.extras, popDeleted.extras));
- // Delete one to force write-all
- engine.deleteFromPending(popDeleted);
- assertEquals("Delete of pending op failed.", 2, engine.getPendingOperationCount());
- // If there's dirty pending data (which there is because we deleted a pending op) this
- // re-writes the entire file.
- engine.writeAllState();
-
- engine.clearAndReadState();
-
- // Validate state read back out.
- assertEquals("Delete of pending op failed.", 2, engine.getPendingOperationCount());
-
- List<SyncStorageEngine.PendingOperation> pops = engine.getPendingOperations();
-
- SyncStorageEngine.PendingOperation popRetrieved = pops.get(0);
- assertEquals(sop.target.account, popRetrieved.target.account);
- assertEquals(sop.target.provider, popRetrieved.target.provider);
- assertEquals(sop.target.service, popRetrieved.target.service);
- assertEquals(sop.target.userId, popRetrieved.target.userId);
- assertEquals(sop.reason, popRetrieved.reason);
- assertEquals(sop.syncSource, popRetrieved.syncSource);
- assertEquals(sop.isExpedited(), popRetrieved.expedited);
- assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
-
- popRetrieved = pops.get(1);
- assertEquals(sop1.target.account, popRetrieved.target.account);
- assertEquals(sop1.target.provider, popRetrieved.target.provider);
- assertEquals(sop1.target.service, popRetrieved.target.service);
- assertEquals(sop1.target.userId, popRetrieved.target.userId);
- assertEquals(sop1.reason, popRetrieved.reason);
- assertEquals(sop1.syncSource, popRetrieved.syncSource);
- assertEquals(sop1.isExpedited(), popRetrieved.expedited);
- assert(android.content.PeriodicSync.syncExtrasEquals(sop1.extras, popRetrieved.extras));
- }
-
- /**
- * Test that we can create, remove and retrieve periodic syncs. Backwards compatibility -
- * periodic syncs with no flex time are no longer used.
- */
- @MediumTest
- public void testPeriodics() throws Exception {
- final Account account1 = new Account("a@example.com", "example.type");
- final Account account2 = new Account("b@example.com", "example.type.2");
- final String authority = "testprovider";
- final Bundle extras1 = new Bundle();
- extras1.putString("a", "1");
- final Bundle extras2 = new Bundle();
- extras2.putString("a", "2");
- final int period1 = 200;
- final int period2 = 1000;
-
- PeriodicSync sync1 = new PeriodicSync(account1, authority, extras1, period1);
- EndPoint end1 = new EndPoint(account1, authority, 0);
-
- PeriodicSync sync2 = new PeriodicSync(account1, authority, extras2, period1);
- PeriodicSync sync3 = new PeriodicSync(account1, authority, extras2, period2);
- PeriodicSync sync4 = new PeriodicSync(account2, authority, extras2, period2);
-
-
-
- removePeriodicSyncs(engine, account1, 0, authority);
- removePeriodicSyncs(engine, account2, 0, authority);
- removePeriodicSyncs(engine, account1, 1, authority);
-
- // this should add two distinct periodic syncs for account1 and one for account2
- engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 0), period1, 0, extras1);
- engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 0), period1, 0, extras2);
- engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 0), period2, 0, extras2);
- engine.updateOrAddPeriodicSync(new EndPoint(account2, authority, 0), period2, 0, extras2);
- // add a second user
- engine.updateOrAddPeriodicSync(new EndPoint(account1, authority, 1), period1, 0, extras2);
-
- List<PeriodicSync> syncs = engine.getPeriodicSyncs(new EndPoint(account1, authority, 0));
-
- assertEquals(2, syncs.size());
-
- assertEquals(sync1, syncs.get(0));
- assertEquals(sync3, syncs.get(1));
-
- engine.removePeriodicSync(new EndPoint(account1, authority, 0), extras1);
-
- syncs = engine.getPeriodicSyncs(new EndPoint(account1, authority, 0));
- assertEquals(1, syncs.size());
- assertEquals(sync3, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(new EndPoint(account2, authority, 0));
- assertEquals(1, syncs.size());
- assertEquals(sync4, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(new EndPoint(sync2.account, sync2.authority, 1));
- assertEquals(1, syncs.size());
- assertEquals(sync2, syncs.get(0));
- }
-
- /**
- * Test that we can create, remove and retrieve periodic syncs with a provided flex time.
- */
- @MediumTest
- public void testPeriodicsV2() throws Exception {
- final Account account1 = new Account("a@example.com", "example.type");
- final Account account2 = new Account("b@example.com", "example.type.2");
- final String authority = "testprovider";
- final Bundle extras1 = new Bundle();
- extras1.putString("a", "1");
- final Bundle extras2 = new Bundle();
- extras2.putString("a", "2");
- final int period1 = 200;
- final int period2 = 1000;
- final int flex1 = 10;
- final int flex2 = 100;
- EndPoint point1 = new EndPoint(account1, authority, 0);
- EndPoint point2 = new EndPoint(account2, authority, 0);
- EndPoint point1User2 = new EndPoint(account1, authority, 1);
-
- PeriodicSync sync1 = new PeriodicSync(account1, authority, extras1, period1, flex1);
- PeriodicSync sync2 = new PeriodicSync(account1, authority, extras2, period1, flex1);
- PeriodicSync sync3 = new PeriodicSync(account1, authority, extras2, period2, flex2);
- PeriodicSync sync4 = new PeriodicSync(account2, authority, extras2, period2, flex2);
-
- EndPoint target1 = new EndPoint(account1, authority, 0);
- EndPoint target2 = new EndPoint(account2, authority, 0);
- EndPoint target1UserB = new EndPoint(account1, authority, 1);
-
- MockContentResolver mockResolver = new MockContentResolver();
-
- SyncStorageEngine engine = SyncStorageEngine.newTestInstance(
- new TestContext(mockResolver, getContext()));
-
- removePeriodicSyncs(engine, account1, 0, authority);
- removePeriodicSyncs(engine, account2, 0, authority);
- removePeriodicSyncs(engine, account1, 1, authority);
-
- // This should add two distinct periodic syncs for account1 and one for account2
- engine.updateOrAddPeriodicSync(target1, period1, flex1, extras1);
- engine.updateOrAddPeriodicSync(target1, period1, flex1, extras2);
- // Edit existing sync and update the period and flex.
- engine.updateOrAddPeriodicSync(target1, period2, flex2, extras2);
- engine.updateOrAddPeriodicSync(target2, period2, flex2, extras2);
- // add a target for a second user.
- engine.updateOrAddPeriodicSync(target1UserB, period1, flex1, extras2);
-
- List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1);
-
- assertEquals(2, syncs.size());
-
- assertEquals(sync1, syncs.get(0));
- assertEquals(sync3, syncs.get(1));
-
- engine.removePeriodicSync(target1, extras1);
-
- syncs = engine.getPeriodicSyncs(target1);
- assertEquals(1, syncs.size());
- assertEquals(sync3, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target2);
- assertEquals(1, syncs.size());
- assertEquals(sync4, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target1UserB);
- assertEquals(1, syncs.size());
- assertEquals(sync2, syncs.get(0));
- }
-
- private void removePeriodicSyncs(SyncStorageEngine engine, Account account, int userId, String authority) {
- EndPoint target = new EndPoint(account, authority, userId);
- engine.setIsSyncable(account, userId, authority, engine.getIsSyncable(account, userId, authority));
- List<PeriodicSync> syncs = engine.getPeriodicSyncs(target);
- for (PeriodicSync sync : syncs) {
- engine.removePeriodicSync(target, sync.extras);
- }
- }
-
@LargeTest
public void testAuthorityPersistence() throws Exception {
final Account account1 = new Account("a@example.com", "example.type");
final Account account2 = new Account("b@example.com", "example.type.2");
final String authority1 = "testprovider1";
final String authority2 = "testprovider2";
- final Bundle extras1 = new Bundle();
- extras1.putString("a", "1");
- final Bundle extras2 = new Bundle();
- extras2.putString("a", "2");
- extras2.putLong("b", 2);
- extras2.putInt("c", 1);
- extras2.putBoolean("d", true);
- extras2.putDouble("e", 1.2);
- extras2.putFloat("f", 4.5f);
- extras2.putParcelable("g", account1);
- final int period1 = 200;
- final int period2 = 1000;
- final int flex1 = 10;
- final int flex2 = 100;
-
- EndPoint point1 = new EndPoint(account1, authority1, 0);
- EndPoint point2 = new EndPoint(account1, authority2, 0);
- EndPoint point3 = new EndPoint(account2, authority1, 0);
-
- PeriodicSync sync1 = new PeriodicSync(account1, authority1, extras1, period1, flex1);
- PeriodicSync sync2 = new PeriodicSync(account1, authority1, extras2, period1, flex1);
- PeriodicSync sync3 = new PeriodicSync(account1, authority2, extras1, period1, flex1);
- PeriodicSync sync4 = new PeriodicSync(account1, authority2, extras2, period2, flex2);
- PeriodicSync sync5 = new PeriodicSync(account2, authority1, extras1, period1, flex1);
-
- EndPoint target1 = new EndPoint(account1, authority1, 0);
- EndPoint target2 = new EndPoint(account1, authority2, 0);
- EndPoint target3 = new EndPoint(account2, authority1, 0);
-
- removePeriodicSyncs(engine, account1, 0, authority1);
- removePeriodicSyncs(engine, account2, 0, authority1);
- removePeriodicSyncs(engine, account1, 0, authority2);
- removePeriodicSyncs(engine, account2, 0, authority2);
engine.setMasterSyncAutomatically(false, 0);
@@ -395,29 +125,9 @@
engine.setIsSyncable(account2, 0, authority2, 0);
engine.setSyncAutomatically(account2, 0, authority2, true);
- engine.updateOrAddPeriodicSync(target1, period1, flex1, extras1);
- engine.updateOrAddPeriodicSync(target1, period1, flex1, extras2);
- engine.updateOrAddPeriodicSync(target2, period1, flex1, extras1);
- engine.updateOrAddPeriodicSync(target2, period2, flex2, extras2);
- engine.updateOrAddPeriodicSync(target3, period1, flex1, extras1);
-
engine.writeAllState();
engine.clearAndReadState();
- List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1);
- assertEquals(2, syncs.size());
- assertEquals(sync1, syncs.get(0));
- assertEquals(sync2, syncs.get(1));
-
- syncs = engine.getPeriodicSyncs(target2);
- assertEquals(2, syncs.size());
- assertEquals(sync3, syncs.get(0));
- assertEquals(sync4, syncs.get(1));
-
- syncs = engine.getPeriodicSyncs(target3);
- assertEquals(1, syncs.size());
- assertEquals(sync5, syncs.get(0));
-
assertEquals(true, engine.getSyncAutomatically(account1, 0, authority1));
assertEquals(true, engine.getSyncAutomatically(account2, 0, authority1));
assertEquals(false, engine.getSyncAutomatically(account1, 0, authority2));
@@ -429,290 +139,6 @@
assertEquals(0, engine.getIsSyncable(account2, 0, authority2));
}
- @SmallTest
- public void testComponentParsing() throws Exception {
-
- byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts version=\"2\" >\n"
- + "<authority id=\"0\" user=\"0\" package=\"" + syncService1.getPackageName() + "\""
- + " class=\"" + syncService1.getClassName() + "\" syncable=\"true\">"
- + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>"
- + "\n</authority>"
- + "</accounts>").getBytes();
-
- File syncDir = getSyncDir();
- syncDir.mkdirs();
- AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
- FileOutputStream fos = accountInfoFile.startWrite();
- fos.write(accountsFileData);
- accountInfoFile.finishWrite(fos);
-
- engine.clearAndReadState();
-
- SyncStorageEngine.AuthorityInfo aInfo = engine.getAuthority(0);
- assertNotNull(aInfo);
-
- // Test service component read
- List<PeriodicSync> syncs = engine.getPeriodicSyncs(
- new SyncStorageEngine.EndPoint(syncService1, 0, 0));
- assertEquals(1, syncs.size());
- assertEquals(true, engine.getIsTargetServiceActive(syncService1, 0));
- }
-
- @SmallTest
- public void testComponentSettings() throws Exception {
- EndPoint target1 = new EndPoint(syncService1, 0, 0);
- engine.updateOrAddPeriodicSync(target1, dayPoll, dayFuzz, Bundle.EMPTY);
-
- engine.setIsTargetServiceActive(target1.service, 0, true);
- boolean active = engine.getIsTargetServiceActive(target1.service, 0);
- assert(active);
-
- engine.setIsTargetServiceActive(target1.service, 1, false);
- active = engine.getIsTargetServiceActive(target1.service, 1);
- assert(!active);
- }
-
- @MediumTest
- /**
- * V2 introduces flex time as well as service components.
- * @throws Exception
- */
- public void testAuthorityParsingV2() throws Exception {
- final Account account = new Account("account1", "type1");
- final String authority1 = "auth1";
- final String authority2 = "auth2";
- final String authority3 = "auth3";
-
- EndPoint target1 = new EndPoint(account, authority1, 0);
- EndPoint target2 = new EndPoint(account, authority2, 0);
- EndPoint target3 = new EndPoint(account, authority3, 0);
- EndPoint target4 = new EndPoint(account, authority3, 1);
-
- PeriodicSync sync1 = new PeriodicSync(account, authority1, Bundle.EMPTY, dayPoll, dayFuzz);
- PeriodicSync sync2 = new PeriodicSync(account, authority2, Bundle.EMPTY, dayPoll, dayFuzz);
- PeriodicSync sync3 = new PeriodicSync(account, authority3, Bundle.EMPTY, dayPoll, dayFuzz);
- PeriodicSync sync1s = new PeriodicSync(account, authority1, Bundle.EMPTY, thousandSecs,
- thousandSecsFuzz);
- PeriodicSync sync2s = new PeriodicSync(account, authority2, Bundle.EMPTY, thousandSecs,
- thousandSecsFuzz);
- PeriodicSync sync3s = new PeriodicSync(account, authority3, Bundle.EMPTY, thousandSecs,
- thousandSecsFuzz);
-
- byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts version=\"2\" >\n"
- + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" >"
- + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>"
- + "\n</authority>"
- + "<authority id=\"1\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth2\" >"
- + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>"
- + "\n</authority>"
- // No user defaults to user 0 - all users.
- + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" >"
- + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>"
- + "\n</authority>"
- + "<authority id=\"3\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth3\" >"
- + "\n<periodicSync period=\"" + dayPoll + "\" flex=\"" + dayFuzz + "\"/>"
- + "\n</authority>"
- + "</accounts>").getBytes();
-
- File syncDir = getSyncDir();
- syncDir.mkdirs();
- AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
- FileOutputStream fos = accountInfoFile.startWrite();
- fos.write(accountsFileData);
- accountInfoFile.finishWrite(fos);
-
- engine.clearAndReadState();
-
- List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1);
- assertEquals("Got incorrect # of syncs", 1, syncs.size());
- assertEquals(sync1, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target2);
- assertEquals(1, syncs.size());
- assertEquals(sync2, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target3);
- assertEquals(1, syncs.size());
- assertEquals(sync3, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target4);
-
- assertEquals(1, syncs.size());
- assertEquals(sync3, syncs.get(0));
-
- // Test empty periodic data.
- accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts version=\"2\">\n"
- + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
- + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n"
- + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
- + "</accounts>\n").getBytes();
-
- accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
- fos = accountInfoFile.startWrite();
- fos.write(accountsFileData);
- accountInfoFile.finishWrite(fos);
-
- engine.clearAndReadState();
-
- syncs = engine.getPeriodicSyncs(target1);
- assertEquals(0, syncs.size());
-
- syncs = engine.getPeriodicSyncs(target2);
- assertEquals(0, syncs.size());
-
- syncs = engine.getPeriodicSyncs(target3);
- assertEquals(0, syncs.size());
-
- accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts version=\"2\">\n"
- + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\">\n"
- + "<periodicSync period=\"1000\" />\n"
- + "</authority>"
- + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\">\n"
- + "<periodicSync period=\"1000\" />\n"
- + "</authority>"
- + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\">\n"
- + "<periodicSync period=\"1000\" />\n"
- + "</authority>"
- + "</accounts>\n").getBytes();
-
- accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
- fos = accountInfoFile.startWrite();
- fos.write(accountsFileData);
- accountInfoFile.finishWrite(fos);
-
- engine.clearAndReadState();
-
- syncs = engine.getPeriodicSyncs(target1);
- assertEquals(1, syncs.size());
- assertEquals(sync1s, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target2);
- assertEquals(1, syncs.size());
- assertEquals(sync2s, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target3);
- assertEquals(1, syncs.size());
- assertEquals(sync3s, syncs.get(0));
- }
-
- @MediumTest
- public void testAuthorityParsing() throws Exception {
- final Account account = new Account("account1", "type1");
- final String authority1 = "auth1";
- final String authority2 = "auth2";
- final String authority3 = "auth3";
- final Bundle extras = new Bundle();
-
- EndPoint target1 = new EndPoint(account, authority1, 0);
- EndPoint target2 = new EndPoint(account, authority2, 0);
- EndPoint target3 = new EndPoint(account, authority3, 0);
- EndPoint target4 = new EndPoint(account, authority3, 1);
-
- PeriodicSync sync1 = new PeriodicSync(account, authority1, extras, (long) (60 * 60 * 24));
- PeriodicSync sync2 = new PeriodicSync(account, authority2, extras, (long) (60 * 60 * 24));
- PeriodicSync sync3 = new PeriodicSync(account, authority3, extras, (long) (60 * 60 * 24));
- PeriodicSync sync1s = new PeriodicSync(account, authority1, extras, 1000);
- PeriodicSync sync2s = new PeriodicSync(account, authority2, extras, 1000);
- PeriodicSync sync3s = new PeriodicSync(account, authority3, extras, 1000);
-
- MockContentResolver mockResolver = new MockContentResolver();
-
- final TestContext testContext = new TestContext(mockResolver, getContext());
-
- byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts>\n"
- + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
- + "<authority id=\"1\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n"
- + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
- + "<authority id=\"3\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
- + "</accounts>\n").getBytes();
-
- File syncDir = getSyncDir();
- syncDir.mkdirs();
- AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
- FileOutputStream fos = accountInfoFile.startWrite();
- fos.write(accountsFileData);
- accountInfoFile.finishWrite(fos);
-
- SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
-
- List<PeriodicSync> syncs = engine.getPeriodicSyncs(target1);
- assertEquals(1, syncs.size());
- assertEquals("expected sync1: " + sync1.toString() + " == sync 2" + syncs.get(0).toString(), sync1, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target2);
- assertEquals(1, syncs.size());
- assertEquals(sync2, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target3);
- assertEquals(1, syncs.size());
- assertEquals(sync3, syncs.get(0));
- syncs = engine.getPeriodicSyncs(target4);
-
-
- assertEquals(1, syncs.size());
- assertEquals(sync3, syncs.get(0));
-
- accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts version=\"2\">\n"
- + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
- + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\" />\n"
- + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\" />\n"
- + "</accounts>\n").getBytes();
-
- accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
- fos = accountInfoFile.startWrite();
- fos.write(accountsFileData);
- accountInfoFile.finishWrite(fos);
-
- engine.clearAndReadState();
-
- syncs = engine.getPeriodicSyncs(target1);
- assertEquals(0, syncs.size());
-
- syncs = engine.getPeriodicSyncs(target2);
- assertEquals(0, syncs.size());
-
- syncs = engine.getPeriodicSyncs(target3);
- assertEquals(0, syncs.size());
-
- accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
- + "<accounts version=\"2\">\n"
- + "<authority id=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\">\n"
- + "<periodicSync period=\"1000\" />\n"
- + "</authority>"
- + "<authority id=\"1\" account=\"account1\" type=\"type1\" authority=\"auth2\">\n"
- + "<periodicSync period=\"1000\" />\n"
- + "</authority>"
- + "<authority id=\"2\" account=\"account1\" type=\"type1\" authority=\"auth3\">\n"
- + "<periodicSync period=\"1000\" />\n"
- + "</authority>"
- + "</accounts>\n").getBytes();
-
- accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
- fos = accountInfoFile.startWrite();
- fos.write(accountsFileData);
- accountInfoFile.finishWrite(fos);
-
- engine.clearAndReadState();
-
- syncs = engine.getPeriodicSyncs(target1);
- assertEquals(1, syncs.size());
- assertEquals(sync1s, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target2);
- assertEquals(1, syncs.size());
- assertEquals(sync2s, syncs.get(0));
-
- syncs = engine.getPeriodicSyncs(target3);
- assertEquals(1, syncs.size());
- assertEquals(sync3s, syncs.get(0));
- }
-
@MediumTest
public void testListenForTicklesParsing() throws Exception {
byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"