blob: 036b21fc187e08cdd49e78d2443d767e800b6b62 [file] [log] [blame]
Kenny Root15a4d2f2010-03-11 18:20:12 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.content;
Fred Quintana307da1a2010-01-21 14:24:20 -080018
19import android.accounts.Account;
Alon Albert57286f92012-10-09 14:21:38 -070020import android.content.pm.PackageManager;
Matthew Williamsfa774182013-06-18 15:44:11 -070021import android.content.ComponentName;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080022import android.content.ContentResolver;
Fred Quintana307da1a2010-01-21 14:24:20 -080023import android.os.Bundle;
24import android.os.SystemClock;
Matthew Williams56dbf8f2013-07-26 12:56:39 -070025import android.util.Log;
Fred Quintana307da1a2010-01-21 14:24:20 -080026
27/**
28 * Value type that represents a sync operation.
Matthew Williamsfa774182013-06-18 15:44:11 -070029 * TODO: This is the class to flesh out with all the scheduling data - metered/unmetered,
30 * transfer-size, etc.
31 * {@hide}
Fred Quintana307da1a2010-01-21 14:24:20 -080032 */
33public class SyncOperation implements Comparable {
Matthew Williams56dbf8f2013-07-26 12:56:39 -070034 public static final String TAG = "SyncManager";
35
Alon Albert57286f92012-10-09 14:21:38 -070036 public static final int REASON_BACKGROUND_DATA_SETTINGS_CHANGED = -1;
37 public static final int REASON_ACCOUNTS_UPDATED = -2;
38 public static final int REASON_SERVICE_CHANGED = -3;
39 public static final int REASON_PERIODIC = -4;
Matthew Williams56dbf8f2013-07-26 12:56:39 -070040 /** Sync started because it has just been set to isSyncable. */
Alon Albert57286f92012-10-09 14:21:38 -070041 public static final int REASON_IS_SYNCABLE = -5;
Matthew Williamsfa774182013-06-18 15:44:11 -070042 /** Sync started because it has just been set to sync automatically. */
Alon Albert57286f92012-10-09 14:21:38 -070043 public static final int REASON_SYNC_AUTO = -6;
Matthew Williamsfa774182013-06-18 15:44:11 -070044 /** Sync started because master sync automatically has been set to true. */
Alon Albert57286f92012-10-09 14:21:38 -070045 public static final int REASON_MASTER_SYNC_AUTO = -7;
46 public static final int REASON_USER_START = -8;
47
48 private static String[] REASON_NAMES = new String[] {
49 "DataSettingsChanged",
50 "AccountsUpdated",
51 "ServiceChanged",
52 "Periodic",
53 "IsSyncable",
54 "AutoSync",
55 "MasterSyncAuto",
56 "UserStart",
57 };
58
Matthew Williams56dbf8f2013-07-26 12:56:39 -070059 public static final int SYNC_TARGET_UNKNOWN = 0;
60 public static final int SYNC_TARGET_ADAPTER = 1;
61 public static final int SYNC_TARGET_SERVICE = 2;
62
Matthew Williams8ef22042013-07-26 12:56:39 -070063 /** Identifying info for the target for this operation. */
Matthew Williams56dbf8f2013-07-26 12:56:39 -070064 public final SyncStorageEngine.EndPoint target;
65 /** Why this sync was kicked off. {@link #REASON_NAMES} */
Alon Albert57286f92012-10-09 14:21:38 -070066 public final int reason;
Matthew Williams56dbf8f2013-07-26 12:56:39 -070067 /** Where this sync was initiated. */
Fred Quintana307da1a2010-01-21 14:24:20 -080068 public int syncSource;
Fred Quintana0c4d04a2010-11-03 17:02:55 -070069 public final boolean allowParallelSyncs;
Fred Quintana307da1a2010-01-21 14:24:20 -080070 public Bundle extras;
71 public final String key;
Fred Quintana307da1a2010-01-21 14:24:20 -080072 public boolean expedited;
Matthew Williams56dbf8f2013-07-26 12:56:39 -070073 /** Bare-bones version of this operation that is persisted across reboots. */
Fred Quintana307da1a2010-01-21 14:24:20 -080074 public SyncStorageEngine.PendingOperation pendingOperation;
Matthew Williamsfa774182013-06-18 15:44:11 -070075 /** Elapsed real time in millis at which to run this sync. */
76 public long latestRunTime;
77 /** Set by the SyncManager in order to delay retries. */
Fred Quintana918339a2010-10-05 14:00:39 -070078 public Long backoff;
Matthew Williamsfa774182013-06-18 15:44:11 -070079 /** Specified by the adapter to delay subsequent sync operations. */
Fred Quintana918339a2010-10-05 14:00:39 -070080 public long delayUntil;
Matthew Williamsfa774182013-06-18 15:44:11 -070081 /**
82 * Elapsed real time in millis when this sync will be run.
83 * Depends on max(backoff, latestRunTime, and delayUntil).
84 */
Fred Quintana918339a2010-10-05 14:00:39 -070085 public long effectiveRunTime;
Matthew Williams56dbf8f2013-07-26 12:56:39 -070086 /** Amount of time before {@link #effectiveRunTime} from which this sync can run. */
Matthew Williamsfa774182013-06-18 15:44:11 -070087 public long flexTime;
Fred Quintana307da1a2010-01-21 14:24:20 -080088
Matthew Williams56dbf8f2013-07-26 12:56:39 -070089 public SyncOperation(Account account, int userId, int reason, int source, String provider,
Matthew Williamsfa774182013-06-18 15:44:11 -070090 Bundle extras, long runTimeFromNow, long flexTime, long backoff,
91 long delayUntil, boolean allowParallelSyncs) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -070092 this.target = new SyncStorageEngine.EndPoint(account, provider, userId);
Alon Albert57286f92012-10-09 14:21:38 -070093 this.reason = reason;
Torne (Richard Coles)4890b082013-08-12 10:26:57 +000094 this.allowParallelSyncs = allowParallelSyncs;
Matthew Williams56dbf8f2013-07-26 12:56:39 -070095 this.key = initialiseOperation(this.target, source, extras, runTimeFromNow, flexTime,
96 backoff, delayUntil);
97 }
98
99 public SyncOperation(ComponentName service, int userId, int reason, int source,
100 Bundle extras, long runTimeFromNow, long flexTime, long backoff,
101 long delayUntil) {
102 this.target = new SyncStorageEngine.EndPoint(service, userId);
103 // Default to true for sync service. The service itself decides how to handle this.
104 this.allowParallelSyncs = true;
105 this.reason = reason;
106 this.key =
107 initialiseOperation(this.target,
108 source, extras, runTimeFromNow, flexTime, backoff, delayUntil);
109 }
110
111 /** Used to reschedule a sync at a new point in time. */
112 SyncOperation(SyncOperation other, long newRunTimeFromNow) {
113 this.target = other.target;
114 this.reason = other.reason;
115 this.expedited = other.expedited;
116 this.allowParallelSyncs = other.allowParallelSyncs;
117 // re-use old flex, but only
118 long newFlexTime = Math.min(other.flexTime, newRunTimeFromNow);
119 this.key =
120 initialiseOperation(this.target,
121 other.syncSource, other.extras,
122 newRunTimeFromNow /* runTimeFromNow*/,
123 newFlexTime /* flexTime */,
124 other.backoff,
125 0L /* delayUntil */);
126 }
127
128 private String initialiseOperation(SyncStorageEngine.EndPoint info, int source, Bundle extras,
129 long runTimeFromNow, long flexTime, long backoff, long delayUntil) {
130 this.syncSource = source;
Fred Quintana307da1a2010-01-21 14:24:20 -0800131 this.extras = new Bundle(extras);
Matthew Williamsfa774182013-06-18 15:44:11 -0700132 cleanBundle(this.extras);
Fred Quintana918339a2010-10-05 14:00:39 -0700133 this.delayUntil = delayUntil;
134 this.backoff = backoff;
Fred Quintana307da1a2010-01-21 14:24:20 -0800135 final long now = SystemClock.elapsedRealtime();
Matthew Williamsfa774182013-06-18 15:44:11 -0700136 if (runTimeFromNow < 0 || isExpedited()) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800137 this.expedited = true;
Matthew Williamsfa774182013-06-18 15:44:11 -0700138 this.latestRunTime = now;
139 this.flexTime = 0;
Fred Quintana307da1a2010-01-21 14:24:20 -0800140 } else {
141 this.expedited = false;
Matthew Williamsfa774182013-06-18 15:44:11 -0700142 this.latestRunTime = now + runTimeFromNow;
143 this.flexTime = flexTime;
Fred Quintana307da1a2010-01-21 14:24:20 -0800144 }
Fred Quintana918339a2010-10-05 14:00:39 -0700145 updateEffectiveRunTime();
Matthew Williams8ef22042013-07-26 12:56:39 -0700146 return toKey(info, this.extras);
Fred Quintana307da1a2010-01-21 14:24:20 -0800147 }
148
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700149 public boolean matchesAuthority(SyncOperation other) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700150 return this.target.matchesSpec(other.target);
Matthew Williamsfa774182013-06-18 15:44:11 -0700151 }
152
153 /**
154 * Make sure the bundle attached to this SyncOperation doesn't have unnecessary
155 * flags set.
156 * @param bundle to clean.
157 */
158 private void cleanBundle(Bundle bundle) {
159 removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_UPLOAD);
160 removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_MANUAL);
161 removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS);
162 removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
163 removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY);
164 removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS);
165 removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_EXPEDITED);
166 removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700167 removeFalseExtra(bundle, ContentResolver.SYNC_EXTRAS_DISALLOW_METERED);
Matthew Williamsfa774182013-06-18 15:44:11 -0700168 }
169
170 private void removeFalseExtra(Bundle bundle, String extraName) {
171 if (!bundle.getBoolean(extraName, false)) {
172 bundle.remove(extraName);
Fred Quintana307da1a2010-01-21 14:24:20 -0800173 }
174 }
175
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700176 /**
177 * Determine whether if this sync operation is running, the provided operation would conflict
178 * with it.
179 * Parallel syncs allow multiple accounts to be synced at the same time.
180 */
181 public boolean isConflict(SyncOperation toRun) {
182 final SyncStorageEngine.EndPoint other = toRun.target;
183 if (target.target_provider) {
184 return target.account.type.equals(other.account.type)
185 && target.provider.equals(other.provider)
186 && target.userId == other.userId
187 && (!allowParallelSyncs
188 || target.account.name.equals(other.account.name));
189 } else {
190 // Ops that target a service default to allow parallel syncs, which is handled by the
191 // service returning SYNC_IN_PROGRESS if they don't.
192 return target.service.equals(other.service) && !allowParallelSyncs;
193 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800194 }
195
Matthew Williamsfa774182013-06-18 15:44:11 -0700196 @Override
Fred Quintana307da1a2010-01-21 14:24:20 -0800197 public String toString() {
Alon Albert57286f92012-10-09 14:21:38 -0700198 return dump(null, true);
Fred Quintana918339a2010-10-05 14:00:39 -0700199 }
200
Alon Albert57286f92012-10-09 14:21:38 -0700201 public String dump(PackageManager pm, boolean useOneLine) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700202 StringBuilder sb = new StringBuilder();
203 if (target.target_provider) {
204 sb.append(target.account.name)
Alon Albert8e285552012-09-17 15:05:27 -0700205 .append(" u")
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700206 .append(target.userId).append(" (")
207 .append(target.account.type)
Alon Albert8e285552012-09-17 15:05:27 -0700208 .append(")")
209 .append(", ")
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700210 .append(target.provider)
211 .append(", ");
212 } else if (target.target_service) {
213 sb.append(target.service.getPackageName())
214 .append(" u")
215 .append(target.userId).append(" (")
216 .append(target.service.getClassName()).append(")")
217 .append(", ");
218 }
219 sb.append(SyncStorageEngine.SOURCES[syncSource])
220 .append(", currentRunTime ")
221 .append(effectiveRunTime);
Fred Quintana918339a2010-10-05 14:00:39 -0700222 if (expedited) {
223 sb.append(", EXPEDITED");
224 }
Alon Albert57286f92012-10-09 14:21:38 -0700225 sb.append(", reason: ");
226 sb.append(reasonToString(pm, reason));
Fred Quintana918339a2010-10-05 14:00:39 -0700227 if (!useOneLine && !extras.keySet().isEmpty()) {
228 sb.append("\n ");
229 extrasToStringBuilder(extras, sb);
230 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800231 return sb.toString();
232 }
233
Alon Albert57286f92012-10-09 14:21:38 -0700234 public static String reasonToString(PackageManager pm, int reason) {
235 if (reason >= 0) {
236 if (pm != null) {
237 final String[] packages = pm.getPackagesForUid(reason);
238 if (packages != null && packages.length == 1) {
239 return packages[0];
240 }
241 final String name = pm.getNameForUid(reason);
242 if (name != null) {
243 return name;
244 }
245 return String.valueOf(reason);
246 } else {
247 return String.valueOf(reason);
248 }
249 } else {
250 final int index = -reason - 1;
251 if (index >= REASON_NAMES.length) {
252 return String.valueOf(reason);
253 } else {
254 return REASON_NAMES[index];
255 }
256 }
257 }
258
Fred Quintana918339a2010-10-05 14:00:39 -0700259 public boolean isInitialization() {
260 return extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
261 }
262
Fred Quintanadc475562012-05-04 15:51:54 -0700263 public boolean isExpedited() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700264 return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false) || expedited;
Fred Quintanadc475562012-05-04 15:51:54 -0700265 }
266
Fred Quintana918339a2010-10-05 14:00:39 -0700267 public boolean ignoreBackoff() {
268 return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false);
269 }
270
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700271 public boolean isNotAllowedOnMetered() {
272 return extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED, false);
273 }
274
Matthew Williamsfa774182013-06-18 15:44:11 -0700275 /** Changed in V3. */
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700276 public static String toKey(SyncStorageEngine.EndPoint info, Bundle extras) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800277 StringBuilder sb = new StringBuilder();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700278 if (info.target_provider) {
279 sb.append("provider: ").append(info.provider);
280 sb.append(" account {name=" + info.account.name
281 + ", user="
282 + info.userId
283 + ", type="
284 + info.account.type
Matthew Williamsfa774182013-06-18 15:44:11 -0700285 + "}");
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700286 } else if (info.target_service) {
Torne (Richard Coles)4890b082013-08-12 10:26:57 +0000287 sb.append("service {package=" )
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700288 .append(info.service.getPackageName())
Torne (Richard Coles)4890b082013-08-12 10:26:57 +0000289 .append(" user=")
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700290 .append(info.userId)
Torne (Richard Coles)4890b082013-08-12 10:26:57 +0000291 .append(", class=")
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700292 .append(info.service.getClassName())
Torne (Richard Coles)4890b082013-08-12 10:26:57 +0000293 .append("}");
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700294 } else {
295 Log.v(TAG, "Converting SyncOperaton to key, invalid target: " + info.toString());
296 return "";
Matthew Williamsfa774182013-06-18 15:44:11 -0700297 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800298 sb.append(" extras: ");
Fred Quintana918339a2010-10-05 14:00:39 -0700299 extrasToStringBuilder(extras, sb);
Fred Quintana307da1a2010-01-21 14:24:20 -0800300 return sb.toString();
301 }
302
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700303 private static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800304 sb.append("[");
305 for (String key : bundle.keySet()) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800306 sb.append(key).append("=").append(bundle.get(key)).append(" ");
307 }
308 sb.append("]");
309 }
310
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700311 public String wakeLockKey() {
312 if (target.target_provider) {
313 return target.account.name + "/" + target.account.type + ":" + target.provider;
314 } else if (target.target_service) {
315 return target.service.getPackageName() + "/" + target.service.getClassName();
316 } else {
317 Log.wtf(TAG, "Invalid target getting wakelock for operation - " + key);
318 return null;
319 }
320 }
321
322 public String wakeLockName() {
323 if (target.target_provider) {
324 return "/" + target.provider
325 + "/" + target.account.type
326 + "/" + target.account.name;
327 } else if (target.target_service) {
328 return "/" + target.service.getPackageName()
329 + "/" + target.service.getClassName();
330 } else {
331 Log.wtf(TAG, "Invalid target getting wakelock name for operation - " + key);
332 return null;
333 }
334 }
335
Matthew Williamsfa774182013-06-18 15:44:11 -0700336 /**
337 * Update the effective run time of this Operation based on latestRunTime (specified at
338 * creation time of sync), delayUntil (specified by SyncAdapter), or backoff (specified by
339 * SyncManager on soft failures).
340 */
Fred Quintana918339a2010-10-05 14:00:39 -0700341 public void updateEffectiveRunTime() {
Matthew Williamsfa774182013-06-18 15:44:11 -0700342 // Regardless of whether we're in backoff or honouring a delayUntil, we still incorporate
343 // the flex time provided by the developer.
344 effectiveRunTime = ignoreBackoff() ?
345 latestRunTime :
346 Math.max(Math.max(latestRunTime, delayUntil), backoff);
Fred Quintana918339a2010-10-05 14:00:39 -0700347 }
348
Matthew Williamsfa774182013-06-18 15:44:11 -0700349 /**
Matthew Williams7986fe42013-07-29 16:56:23 -0700350 * SyncOperations are sorted based on their earliest effective run time.
351 * This comparator is used to sort the SyncOps at a given time when
352 * deciding which to run, so earliest run time is the best criteria.
Matthew Williamsfa774182013-06-18 15:44:11 -0700353 */
354 @Override
Fred Quintana307da1a2010-01-21 14:24:20 -0800355 public int compareTo(Object o) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700356 SyncOperation other = (SyncOperation) o;
Fred Quintana918339a2010-10-05 14:00:39 -0700357 if (expedited != other.expedited) {
358 return expedited ? -1 : 1;
359 }
Matthew Williams7986fe42013-07-29 16:56:23 -0700360 long thisIntervalStart = Math.max(effectiveRunTime - flexTime, 0);
361 long otherIntervalStart = Math.max(
362 other.effectiveRunTime - other.flexTime, 0);
363 if (thisIntervalStart < otherIntervalStart) {
364 return -1;
365 } else if (otherIntervalStart < thisIntervalStart) {
366 return 1;
367 } else {
Fred Quintana307da1a2010-01-21 14:24:20 -0800368 return 0;
369 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800370 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700371
372 // TODO: Test this to make sure that casting to object doesn't lose the type info for EventLog.
373 public Object[] toEventLog(int event) {
374 Object[] logArray = new Object[4];
375 logArray[1] = event;
376 logArray[2] = syncSource;
377 if (target.target_provider) {
378 logArray[0] = target.provider;
379 logArray[3] = target.account.name.hashCode();
380 } else if (target.target_service) {
381 logArray[0] = target.service.getPackageName();
382 logArray[3] = target.service.hashCode();
383 } else {
384 Log.wtf(TAG, "sync op with invalid target: " + key);
385 }
386 return logArray;
387 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800388}