blob: 5d9388281797112dcf0e498bd1ec9c3651ddfe29 [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,
79 0 /* delay */,
80 0 /* flex */,
81 backoff != null ? backoff.first : 0,
82 mSyncStorageEngine.getDelayUntilTime(info),
83 syncAdapterInfo.type.allowParallelSyncs());
84 operationToAdd.expedited = op.expedited;
85 operationToAdd.pendingOperation = op;
86 add(operationToAdd, op);
87 } else if (info.target_service) {
88 try {
89 mPackageManager.getServiceInfo(info.service, 0);
90 } catch (PackageManager.NameNotFoundException e) {
91 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Matthew Williams8ef22042013-07-26 12:56:39 -070092 Log.w(TAG, "Missing sync service for authority " + op.target);
Matthew Williams56dbf8f2013-07-26 12:56:39 -070093 }
94 continue;
95 }
96 operationToAdd = new SyncOperation(
97 info.service, info.userId, op.reason, op.syncSource,
98 op.extras,
99 0 /* delay */,
100 0 /* flex */,
101 backoff != null ? backoff.first : 0,
102 mSyncStorageEngine.getDelayUntilTime(info));
103 operationToAdd.expedited = op.expedited;
104 operationToAdd.pendingOperation = op;
105 add(operationToAdd, op);
Fred Quintana0c4d04a2010-11-03 17:02:55 -0700106 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800107 }
108 }
109
110 public boolean add(SyncOperation operation) {
111 return add(operation, null /* this is not coming from the database */);
112 }
113
Matthew Williamsfa774182013-06-18 15:44:11 -0700114 /**
115 * Adds a SyncOperation to the queue and creates a PendingOperation object to track that sync.
116 * If an operation is added that already exists, the existing operation is updated if the newly
117 * added operation occurs before (or the interval overlaps).
118 */
Fred Quintana307da1a2010-01-21 14:24:20 -0800119 private boolean add(SyncOperation operation,
120 SyncStorageEngine.PendingOperation pop) {
Matthew Williamsfa774182013-06-18 15:44:11 -0700121 // If an operation with the same key exists and this one should run sooner/overlaps,
122 // replace the run interval of the existing operation with this new one.
123 // Complications: what if the existing operation is expedited but the new operation has an
124 // earlier run time? Will not be a problem for periodic syncs (no expedited flag), and for
125 // one-off syncs we only change it if the new sync is sooner.
Fred Quintana307da1a2010-01-21 14:24:20 -0800126 final String operationKey = operation.key;
127 final SyncOperation existingOperation = mOperationsMap.get(operationKey);
128
129 if (existingOperation != null) {
130 boolean changed = false;
Matthew Williamsfa774182013-06-18 15:44:11 -0700131 if (operation.compareTo(existingOperation) <= 0 ) {
132 existingOperation.expedited = operation.expedited;
133 long newRunTime =
134 Math.min(existingOperation.latestRunTime, operation.latestRunTime);
135 // Take smaller runtime.
136 existingOperation.latestRunTime = newRunTime;
137 // Take newer flextime.
138 existingOperation.flexTime = operation.flexTime;
139 changed = true;
Fred Quintana307da1a2010-01-21 14:24:20 -0800140 }
141 return changed;
142 }
143
144 operation.pendingOperation = pop;
Matthew Williamsfa774182013-06-18 15:44:11 -0700145 // Don't update the PendingOp if one already exists. This really is just a placeholder,
146 // no actual scheduling info is placed here.
Fred Quintana307da1a2010-01-21 14:24:20 -0800147 if (operation.pendingOperation == null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700148 pop = mSyncStorageEngine.insertIntoPending(operation);
Fred Quintana307da1a2010-01-21 14:24:20 -0800149 if (pop == null) {
150 throw new IllegalStateException("error adding pending sync operation "
151 + operation);
152 }
153 operation.pendingOperation = pop;
154 }
155
156 mOperationsMap.put(operationKey, operation);
157 return true;
158 }
159
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700160 public void removeUserLocked(int userId) {
Amith Yamasani13593602012-03-22 16:16:17 -0700161 ArrayList<SyncOperation> opsToRemove = new ArrayList<SyncOperation>();
162 for (SyncOperation op : mOperationsMap.values()) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700163 if (op.target.userId == userId) {
Amith Yamasani13593602012-03-22 16:16:17 -0700164 opsToRemove.add(op);
165 }
166 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700167 for (SyncOperation op : opsToRemove) {
168 remove(op);
169 }
Amith Yamasani13593602012-03-22 16:16:17 -0700170 }
171
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800172 /**
173 * Remove the specified operation if it is in the queue.
174 * @param operation the operation to remove
175 */
Fred Quintana307da1a2010-01-21 14:24:20 -0800176 public void remove(SyncOperation operation) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700177 boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
Fred Quintana307da1a2010-01-21 14:24:20 -0800178 SyncOperation operationToRemove = mOperationsMap.remove(operation.key);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700179 if (isLoggable) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700180 Log.v(TAG, "Attempting to remove: " + operation.key);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700181 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800182 if (operationToRemove == null) {
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700183 if (isLoggable) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700184 Log.v(TAG, "Could not find: " + operation.key);
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700185 }
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800186 return;
187 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800188 if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) {
189 final String errorMessage = "unable to find pending row for " + operationToRemove;
190 Log.e(TAG, errorMessage, new IllegalStateException(errorMessage));
191 }
192 }
193
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700194 /** Reset backoffs for all operations in the queue. */
195 public void clearBackoffs() {
196 for (SyncOperation op : mOperationsMap.values()) {
197 op.backoff = 0L;
198 op.updateEffectiveRunTime();
199 }
200 }
201
202 public void onBackoffChanged(SyncStorageEngine.EndPoint target, long backoff) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700203 // For each op that matches the target of the changed op, update its
Fred Quintana918339a2010-10-05 14:00:39 -0700204 // backoff and effectiveStartTime
Fred Quintana307da1a2010-01-21 14:24:20 -0800205 for (SyncOperation op : mOperationsMap.values()) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700206 if (op.target.matchesSpec(target)) {
Fred Quintana918339a2010-10-05 14:00:39 -0700207 op.backoff = backoff;
208 op.updateEffectiveRunTime();
Fred Quintana307da1a2010-01-21 14:24:20 -0800209 }
210 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800211 }
212
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700213 public void onDelayUntilTimeChanged(SyncStorageEngine.EndPoint target, long delayUntil) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700214 // for each op that matches the target info of the provided op, change the delay time.
Fred Quintana918339a2010-10-05 14:00:39 -0700215 for (SyncOperation op : mOperationsMap.values()) {
Matthew Williams8ef22042013-07-26 12:56:39 -0700216 if (op.target.matchesSpec(target)) {
Fred Quintana918339a2010-10-05 14:00:39 -0700217 op.delayUntil = delayUntil;
218 op.updateEffectiveRunTime();
219 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800220 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800221 }
222
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700223 /**
224 * Remove all of the SyncOperations associated with a given target.
225 *
226 * @param info target object provided here can have null Account/provider. This is the case
227 * where you want to remove all ops associated with a provider (null Account) or all ops
228 * associated with an account (null provider).
229 * @param extras option bundle to include to further specify which operation to remove. If this
230 * bundle contains sync settings flags, they are ignored.
231 */
232 public void remove(final SyncStorageEngine.EndPoint info, Bundle extras) {
Fred Quintana307da1a2010-01-21 14:24:20 -0800233 Iterator<Map.Entry<String, SyncOperation>> entries = mOperationsMap.entrySet().iterator();
234 while (entries.hasNext()) {
235 Map.Entry<String, SyncOperation> entry = entries.next();
236 SyncOperation syncOperation = entry.getValue();
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700237 final SyncStorageEngine.EndPoint opInfo = syncOperation.target;
Matthew Williams8ef22042013-07-26 12:56:39 -0700238 if (!opInfo.matchesSpec(info)) {
Fred Quintanac5d1c6d2010-01-27 12:17:49 -0800239 continue;
240 }
Matthew Williams56dbf8f2013-07-26 12:56:39 -0700241 if (extras != null
242 && !SyncManager.syncExtrasEquals(
243 syncOperation.extras,
244 extras,
245 false /* no config flags*/)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800246 continue;
247 }
Fred Quintana307da1a2010-01-21 14:24:20 -0800248 entries.remove();
249 if (!mSyncStorageEngine.deleteFromPending(syncOperation.pendingOperation)) {
250 final String errorMessage = "unable to find pending row for " + syncOperation;
251 Log.e(TAG, errorMessage, new IllegalStateException(errorMessage));
252 }
253 }
254 }
255
Jeff Sharkeya706e2f2012-10-16 12:02:42 -0700256 public Collection<SyncOperation> getOperations() {
257 return mOperationsMap.values();
258 }
259
Fred Quintana307da1a2010-01-21 14:24:20 -0800260 public void dump(StringBuilder sb) {
Fred Quintana918339a2010-10-05 14:00:39 -0700261 final long now = SystemClock.elapsedRealtime();
Fred Quintana307da1a2010-01-21 14:24:20 -0800262 sb.append("SyncQueue: ").append(mOperationsMap.size()).append(" operation(s)\n");
263 for (SyncOperation operation : mOperationsMap.values()) {
Fred Quintana918339a2010-10-05 14:00:39 -0700264 sb.append(" ");
265 if (operation.effectiveRunTime <= now) {
266 sb.append("READY");
267 } else {
268 sb.append(DateUtils.formatElapsedTime((operation.effectiveRunTime - now) / 1000));
269 }
270 sb.append(" - ");
Alon Albert57286f92012-10-09 14:21:38 -0700271 sb.append(operation.dump(mPackageManager, false)).append("\n");
Fred Quintana307da1a2010-01-21 14:24:20 -0800272 }
273 }
274}