blob: 587de1c342627fb6ed5a870c0a138ddb32661454 [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
Alon Albert57286f92012-10-09 14:21:38 -070019import android.content.pm.PackageManager;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080020import android.content.SyncAdapterType;
21import android.content.SyncAdaptersCache;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070022import android.content.pm.RegisteredServicesCache.ServiceInfo;
Matthew Williams56dbf8f2013-07-26 12:56:39 -070023import android.os.Bundle;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070024import android.os.SystemClock;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070025import android.text.format.DateUtils;
26import android.util.Log;
27import android.util.Pair;
28
29import com.google.android.collect.Maps;
Fred Quintana307da1a2010-01-21 14:24:20 -080030
Fred Quintana307da1a2010-01-21 14:24:20 -080031import java.util.ArrayList;
Jeff Sharkeya706e2f2012-10-16 12:02:42 -070032import java.util.Collection;
Fred Quintana918339a2010-10-05 14:00:39 -070033import java.util.HashMap;
Fred Quintana307da1a2010-01-21 14:24:20 -080034import java.util.Iterator;
Fred Quintana918339a2010-10-05 14:00:39 -070035import java.util.Map;
Fred Quintana307da1a2010-01-21 14:24:20 -080036
37/**
Jeff Sharkeya706e2f2012-10-16 12:02:42 -070038 * Queue of pending sync operations. Not inherently thread safe, external
39 * callers are responsible for locking.
Fred Quintana307da1a2010-01-21 14:24:20 -080040 *
41 * @hide
42 */
43public class SyncQueue {
44 private static final String TAG = "SyncManager";
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070045 private final SyncStorageEngine mSyncStorageEngine;
46 private final SyncAdaptersCache mSyncAdapters;
Alon Albert57286f92012-10-09 14:21:38 -070047 private final PackageManager mPackageManager;
Fred Quintana307da1a2010-01-21 14:24:20 -080048
49 // A Map of SyncOperations operationKey -> SyncOperation that is designed for
50 // quick lookup of an enqueued SyncOperation.
Jeff Sharkeya706e2f2012-10-16 12:02:42 -070051 private final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap();
Fred Quintana307da1a2010-01-21 14:24:20 -080052
Alon Albert57286f92012-10-09 14:21:38 -070053 public SyncQueue(PackageManager packageManager, SyncStorageEngine syncStorageEngine,
54 final SyncAdaptersCache syncAdapters) {
55 mPackageManager = packageManager;
Fred Quintana307da1a2010-01-21 14:24:20 -080056 mSyncStorageEngine = syncStorageEngine;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070057 mSyncAdapters = syncAdapters;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070058 }
59
60 public void addPendingOperations(int userId) {
61 for (SyncStorageEngine.PendingOperation op : mSyncStorageEngine.getPendingOperations()) {
Matthew Williams8ef22042013-07-26 12:56:39 -070062 final SyncStorageEngine.EndPoint info = op.target;
Matthew Williams56dbf8f2013-07-26 12:56:39 -070063 if (info.userId != userId) continue;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070064
Matthew Williams56dbf8f2013-07-26 12:56:39 -070065 final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(info);
66 SyncOperation operationToAdd;
67 if (info.target_provider) {
68 final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo(
69 SyncAdapterType.newKey(info.provider, info.account.type), info.userId);
70 if (syncAdapterInfo == null) {
71 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Matthew Williams8ef22042013-07-26 12:56:39 -070072 Log.v(TAG, "Missing sync adapter info for authority " + op.target);
Matthew Williams56dbf8f2013-07-26 12:56:39 -070073 }
74 continue;
75 }
76 operationToAdd = new SyncOperation(
77 info.account, info.userId, op.reason, op.syncSource, info.provider,
78 op.extras,
Matthew Williams64280462014-01-09 10:49:23 -080079 op.expedited ? -1 : 0 /* delay */,
Matthew Williams56dbf8f2013-07-26 12:56:39 -070080 0 /* flex */,
Matthew Williams64280462014-01-09 10:49:23 -080081 backoff != null ? backoff.first : 0L,
Matthew Williams56dbf8f2013-07-26 12:56:39 -070082 mSyncStorageEngine.getDelayUntilTime(info),
83 syncAdapterInfo.type.allowParallelSyncs());
Matthew Williams56dbf8f2013-07-26 12:56:39 -070084 operationToAdd.pendingOperation = op;
85 add(operationToAdd, op);
86 } else if (info.target_service) {
87 try {
88 mPackageManager.getServiceInfo(info.service, 0);
89 } catch (PackageManager.NameNotFoundException e) {
90 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Matthew Williams8ef22042013-07-26 12:56:39 -070091 Log.w(TAG, "Missing sync service for authority " + op.target);
Matthew Williams56dbf8f2013-07-26 12:56:39 -070092 }
93 continue;
94 }
95 operationToAdd = new SyncOperation(
96 info.service, info.userId, op.reason, op.syncSource,
97 op.extras,
Matthew Williams64280462014-01-09 10:49:23 -080098 op.expedited ? -1 : 0 /* delay */,
Matthew Williams56dbf8f2013-07-26 12:56:39 -070099 0 /* flex */,
100 backoff != null ? backoff.first : 0,
101 mSyncStorageEngine.getDelayUntilTime(info));
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700102 operationToAdd.pendingOperation = op;
103 add(operationToAdd, op);
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700104 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800105 }
106 }
107
108 public boolean add(SyncOperation operation) {
109 return add(operation, null /* this is not coming from the database */);
110 }
111
Matthew Williamsfa774182013-06-18 15:44:11 -0700112 /**
113 * Adds a SyncOperation to the queue and creates a PendingOperation object to track that sync.
114 * If an operation is added that already exists, the existing operation is updated if the newly
115 * added operation occurs before (or the interval overlaps).
116 */
Fred Quintana307da1a2010-01-21 14:24:20 -0800117 private boolean add(SyncOperation operation,
118 SyncStorageEngine.PendingOperation pop) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700119 // If an operation with the same key exists and this one should run sooner/overlaps,
120 // replace the run interval of the existing operation with this new one.
121 // Complications: what if the existing operation is expedited but the new operation has an
122 // earlier run time? Will not be a problem for periodic syncs (no expedited flag), and for
123 // one-off syncs we only change it if the new sync is sooner.
Fred Quintana307da1a2010-01-21 14:24:20 -0800124 final String operationKey = operation.key;
125 final SyncOperation existingOperation = mOperationsMap.get(operationKey);
126
127 if (existingOperation != null) {
128 boolean changed = false;
Matthew Williamsfa774182013-06-18 15:44:11 -0700129 if (operation.compareTo(existingOperation) <= 0 ) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700130 long newRunTime =
131 Math.min(existingOperation.latestRunTime, operation.latestRunTime);
132 // Take smaller runtime.
133 existingOperation.latestRunTime = newRunTime;
134 // Take newer flextime.
135 existingOperation.flexTime = operation.flexTime;
136 changed = true;
Fred Quintana307da1a2010-01-21 14:24:20 -0800137 }
138 return changed;
139 }
140
141 operation.pendingOperation = pop;
Matthew Williamsfa774182013-06-18 15:44:11 -0700142 // Don't update the PendingOp if one already exists. This really is just a placeholder,
143 // no actual scheduling info is placed here.
Fred Quintana307da1a2010-01-21 14:24:20 -0800144 if (operation.pendingOperation == null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700145 pop = mSyncStorageEngine.insertIntoPending(operation);
Fred Quintana307da1a2010-01-21 14:24:20 -0800146 if (pop == null) {
147 throw new IllegalStateException("error adding pending sync operation "
148 + operation);
149 }
150 operation.pendingOperation = pop;
151 }
152
153 mOperationsMap.put(operationKey, operation);
154 return true;
155 }
156
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700157 public void removeUserLocked(int userId) {
Amith Yamasani13593602012-03-22 16:16:17 -0700158 ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>();
159 for (SyncOperation op : mOperationsMap.values()) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700160 if (op.target.userId == userId) {
Amith Yamasani13593602012-03-22 16:16:17 -0700161 opsToRemove.add(op);
162 }
163 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700164 for (SyncOperation op : opsToRemove) {
165 remove(op);
166 }
Amith Yamasani13593602012-03-22 16:16:17 -0700167 }
168
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800169 /**
170 * Remove the specified operation if it is in the queue.
171 * @param operation the operation to remove
172 */
Fred Quintana307da1a2010-01-21 14:24:20 -0800173 public void remove(SyncOperation operation) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700174 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
Fred Quintana307da1a2010-01-21 14:24:20 -0800175 SyncOperation operationToRemove = mOperationsMap.remove(operation.key);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700176 if (isLoggable) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700177 Log.v(TAG, "Attempting to remove: " + operation.key);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700178 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800179 if (operationToRemove == null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700180 if (isLoggable) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700181 Log.v(TAG, "Could not find: " + operation.key);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700182 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800183 return;
184 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800185 if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) {
186 final String errorMessage = "unable to find pending row for " + operationToRemove;
187 Log.e(TAG, errorMessage, new IllegalStateException(errorMessage));
188 }
189 }
190
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700191 /** Reset backoffs for all operations in the queue. */
192 public void clearBackoffs() {
193 for (SyncOperation op : mOperationsMap.values()) {
194 op.backoff = 0L;
195 op.updateEffectiveRunTime();
196 }
197 }
198
199 public void onBackoffChanged(SyncStorageEngine.EndPoint target, long backoff) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700200 // For each op that matches the target of the changed op, update its
Fred Quintana918339a2010-10-05 14:00:39 -0700201 // backoff and effectiveStartTime
Fred Quintana307da1a2010-01-21 14:24:20 -0800202 for (SyncOperation op : mOperationsMap.values()) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700203 if (op.target.matchesSpec(target)) {
Fred Quintana918339a2010-10-05 14:00:39 -0700204 op.backoff = backoff;
205 op.updateEffectiveRunTime();
Fred Quintana307da1a2010-01-21 14:24:20 -0800206 }
207 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800208 }
209
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700210 public void onDelayUntilTimeChanged(SyncStorageEngine.EndPoint target, long delayUntil) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700211 // for each op that matches the target info of the provided op, change the delay time.
Fred Quintana918339a2010-10-05 14:00:39 -0700212 for (SyncOperation op : mOperationsMap.values()) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700213 if (op.target.matchesSpec(target)) {
Fred Quintana918339a2010-10-05 14:00:39 -0700214 op.delayUntil = delayUntil;
215 op.updateEffectiveRunTime();
216 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800217 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800218 }
219
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700220 /**
221 * Remove all of the SyncOperations associated with a given target.
222 *
223 * @param info target object provided here can have null Account/provider. This is the case
224 * where you want to remove all ops associated with a provider (null Account) or all ops
225 * associated with an account (null provider).
226 * @param extras option bundle to include to further specify which operation to remove. If this
227 * bundle contains sync settings flags, they are ignored.
228 */
229 public void remove(final SyncStorageEngine.EndPoint info, Bundle extras) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800230 Iterator<Map.Entry<String, SyncOperation>> entries = mOperationsMap.entrySet().iterator();
231 while (entries.hasNext()) {
232 Map.Entry<String, SyncOperation> entry = entries.next();
233 SyncOperation syncOperation = entry.getValue();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700234 final SyncStorageEngine.EndPoint opInfo = syncOperation.target;
Matthew Williams8ef22042013-07-26 12:56:39 -0700235 if (!opInfo.matchesSpec(info)) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800236 continue;
237 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700238 if (extras != null
239 && !SyncManager.syncExtrasEquals(
240 syncOperation.extras,
241 extras,
242 false /* no config flags*/)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800243 continue;
244 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800245 entries.remove();
246 if (!mSyncStorageEngine.deleteFromPending(syncOperation.pendingOperation)) {
247 final String errorMessage = "unable to find pending row for " + syncOperation;
248 Log.e(TAG, errorMessage, new IllegalStateException(errorMessage));
249 }
250 }
251 }
252
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700253 public Collection<SyncOperation> getOperations() {
254 return mOperationsMap.values();
255 }
256
Fred Quintana307da1a2010-01-21 14:24:20 -0800257 public void dump(StringBuilder sb) {
Fred Quintana918339a2010-10-05 14:00:39 -0700258 final long now = SystemClock.elapsedRealtime();
Fred Quintana307da1a2010-01-21 14:24:20 -0800259 sb.append("SyncQueue: ").append(mOperationsMap.size()).append(" operation(s)\n");
260 for (SyncOperation operation : mOperationsMap.values()) {
Fred Quintana918339a2010-10-05 14:00:39 -0700261 sb.append(" ");
262 if (operation.effectiveRunTime <= now) {
263 sb.append("READY");
264 } else {
265 sb.append(DateUtils.formatElapsedTime((operation.effectiveRunTime - now) / 1000));
266 }
267 sb.append(" - ");
Alon Albert57286f92012-10-09 14:21:38 -0700268 sb.append(operation.dump(mPackageManager, false)).append("\n");
Fred Quintana307da1a2010-01-21 14:24:20 -0800269 }
270 }
271}