• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.content;
18 
19 import com.google.android.collect.Maps;
20 
21 import android.content.pm.RegisteredServicesCache;
22 import android.os.SystemClock;
23 import android.text.format.DateUtils;
24 import android.util.Pair;
25 import android.util.Log;
26 import android.accounts.Account;
27 
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.Map;
32 
33 /**
34  *
35  * @hide
36  */
37 public class SyncQueue {
38     private static final String TAG = "SyncManager";
39     private SyncStorageEngine mSyncStorageEngine;
40 
41     // A Map of SyncOperations operationKey -> SyncOperation that is designed for
42     // quick lookup of an enqueued SyncOperation.
43     public final HashMap<String, SyncOperation> mOperationsMap = Maps.newHashMap();
44 
SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters)45     public SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters) {
46         mSyncStorageEngine = syncStorageEngine;
47         ArrayList<SyncStorageEngine.PendingOperation> ops
48                 = mSyncStorageEngine.getPendingOperations();
49         final int N = ops.size();
50         for (int i=0; i<N; i++) {
51             SyncStorageEngine.PendingOperation op = ops.get(i);
52             final Pair<Long, Long> backoff = syncStorageEngine.getBackoff(op.account, op.authority);
53             final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
54                     syncAdapters.getServiceInfo(
55                             SyncAdapterType.newKey(op.authority, op.account.type));
56             if (syncAdapterInfo == null) {
57                 continue;
58             }
59             SyncOperation syncOperation = new SyncOperation(
60                     op.account, op.syncSource, op.authority, op.extras, 0 /* delay */,
61                     backoff != null ? backoff.first : 0,
62                     syncStorageEngine.getDelayUntilTime(op.account, op.authority),
63                     syncAdapterInfo.type.allowParallelSyncs());
64             syncOperation.expedited = op.expedited;
65             syncOperation.pendingOperation = op;
66             add(syncOperation, op);
67         }
68     }
69 
add(SyncOperation operation)70     public boolean add(SyncOperation operation) {
71         return add(operation, null /* this is not coming from the database */);
72     }
73 
add(SyncOperation operation, SyncStorageEngine.PendingOperation pop)74     private boolean add(SyncOperation operation,
75             SyncStorageEngine.PendingOperation pop) {
76         // - if an operation with the same key exists and this one should run earlier,
77         //   update the earliestRunTime of the existing to the new time
78         // - if an operation with the same key exists and if this one should run
79         //   later, ignore it
80         // - if no operation exists then add the new one
81         final String operationKey = operation.key;
82         final SyncOperation existingOperation = mOperationsMap.get(operationKey);
83 
84         if (existingOperation != null) {
85             boolean changed = false;
86             if (existingOperation.expedited == operation.expedited) {
87                 final long newRunTime =
88                         Math.min(existingOperation.earliestRunTime, operation.earliestRunTime);
89                 if (existingOperation.earliestRunTime != newRunTime) {
90                     existingOperation.earliestRunTime = newRunTime;
91                     changed = true;
92                 }
93             } else {
94                 if (operation.expedited) {
95                     existingOperation.expedited = true;
96                     changed = true;
97                 }
98             }
99             return changed;
100         }
101 
102         operation.pendingOperation = pop;
103         if (operation.pendingOperation == null) {
104             pop = new SyncStorageEngine.PendingOperation(
105                             operation.account, operation.syncSource,
106                             operation.authority, operation.extras, operation.expedited);
107             pop = mSyncStorageEngine.insertIntoPending(pop);
108             if (pop == null) {
109                 throw new IllegalStateException("error adding pending sync operation "
110                         + operation);
111             }
112             operation.pendingOperation = pop;
113         }
114 
115         mOperationsMap.put(operationKey, operation);
116         return true;
117     }
118 
119     /**
120      * Remove the specified operation if it is in the queue.
121      * @param operation the operation to remove
122      */
remove(SyncOperation operation)123     public void remove(SyncOperation operation) {
124         SyncOperation operationToRemove = mOperationsMap.remove(operation.key);
125         if (operationToRemove == null) {
126             return;
127         }
128         if (!mSyncStorageEngine.deleteFromPending(operationToRemove.pendingOperation)) {
129             final String errorMessage = "unable to find pending row for " + operationToRemove;
130             Log.e(TAG, errorMessage, new IllegalStateException(errorMessage));
131         }
132     }
133 
onBackoffChanged(Account account, String providerName, long backoff)134     public void onBackoffChanged(Account account, String providerName, long backoff) {
135         // for each op that matches the account and provider update its
136         // backoff and effectiveStartTime
137         for (SyncOperation op : mOperationsMap.values()) {
138             if (op.account.equals(account) && op.authority.equals(providerName)) {
139                 op.backoff = backoff;
140                 op.updateEffectiveRunTime();
141             }
142         }
143     }
144 
onDelayUntilTimeChanged(Account account, String providerName, long delayUntil)145     public void onDelayUntilTimeChanged(Account account, String providerName, long delayUntil) {
146         // for each op that matches the account and provider update its
147         // delayUntilTime and effectiveStartTime
148         for (SyncOperation op : mOperationsMap.values()) {
149             if (op.account.equals(account) && op.authority.equals(providerName)) {
150                 op.delayUntil = delayUntil;
151                 op.updateEffectiveRunTime();
152             }
153         }
154     }
155 
remove(Account account, String authority)156     public void remove(Account account, String authority) {
157         Iterator<Map.Entry<String, SyncOperation>> entries = mOperationsMap.entrySet().iterator();
158         while (entries.hasNext()) {
159             Map.Entry<String, SyncOperation> entry = entries.next();
160             SyncOperation syncOperation = entry.getValue();
161             if (account != null && !syncOperation.account.equals(account)) {
162                 continue;
163             }
164             if (authority != null && !syncOperation.authority.equals(authority)) {
165                 continue;
166             }
167             entries.remove();
168             if (!mSyncStorageEngine.deleteFromPending(syncOperation.pendingOperation)) {
169                 final String errorMessage = "unable to find pending row for " + syncOperation;
170                 Log.e(TAG, errorMessage, new IllegalStateException(errorMessage));
171             }
172         }
173     }
174 
dump(StringBuilder sb)175     public void dump(StringBuilder sb) {
176         final long now = SystemClock.elapsedRealtime();
177         sb.append("SyncQueue: ").append(mOperationsMap.size()).append(" operation(s)\n");
178         for (SyncOperation operation : mOperationsMap.values()) {
179             sb.append("  ");
180             if (operation.effectiveRunTime <= now) {
181                 sb.append("READY");
182             } else {
183                 sb.append(DateUtils.formatElapsedTime((operation.effectiveRunTime - now) / 1000));
184             }
185             sb.append(" - ");
186             sb.append(operation.dump(false)).append("\n");
187         }
188     }
189 }
190