1 /*
2  * Copyright (C) 2008 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 com.android.server.content;
18 
19 import android.accounts.Account;
20 import android.accounts.AccountAndUser;
21 import android.accounts.AccountManager;
22 import android.accounts.AccountManagerInternal;
23 import android.app.ActivityManager;
24 import android.app.AppGlobals;
25 import android.app.Notification;
26 import android.app.NotificationManager;
27 import android.app.PendingIntent;
28 import android.app.job.JobInfo;
29 import android.app.job.JobScheduler;
30 import android.content.BroadcastReceiver;
31 import android.content.ComponentName;
32 import android.content.ContentResolver;
33 import android.content.Context;
34 import android.content.ISyncAdapter;
35 import android.content.ISyncContext;
36 import android.content.Intent;
37 import android.content.IntentFilter;
38 import android.content.PeriodicSync;
39 import android.content.ServiceConnection;
40 import android.content.SyncActivityTooManyDeletes;
41 import android.content.SyncAdapterType;
42 import android.content.SyncAdaptersCache;
43 import android.content.SyncInfo;
44 import android.content.SyncResult;
45 import android.content.SyncStatusInfo;
46 import android.content.pm.ApplicationInfo;
47 import android.content.pm.PackageInfo;
48 import android.content.pm.PackageManager;
49 import android.content.pm.PackageManager.NameNotFoundException;
50 import android.content.pm.PackageManagerInternal;
51 import android.content.pm.ProviderInfo;
52 import android.content.pm.RegisteredServicesCache;
53 import android.content.pm.RegisteredServicesCacheListener;
54 import android.content.pm.ResolveInfo;
55 import android.content.pm.UserInfo;
56 import android.database.ContentObserver;
57 import android.net.ConnectivityManager;
58 import android.net.NetworkInfo;
59 import android.net.TrafficStats;
60 import android.os.BatteryStats;
61 import android.os.Bundle;
62 import android.os.Handler;
63 import android.os.IBinder;
64 import android.os.Looper;
65 import android.os.Message;
66 import android.os.Messenger;
67 import android.os.PowerManager;
68 import android.os.RemoteCallback;
69 import android.os.RemoteException;
70 import android.os.ServiceManager;
71 import android.os.SystemClock;
72 import android.os.SystemProperties;
73 import android.os.UserHandle;
74 import android.os.UserManager;
75 import android.os.WorkSource;
76 import android.provider.Settings;
77 import android.text.format.DateUtils;
78 import android.text.format.Time;
79 import android.util.EventLog;
80 import android.util.Log;
81 import android.util.Pair;
82 import android.util.Slog;
83 
84 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
85 import com.android.internal.notification.SystemNotificationChannels;
86 import com.android.internal.util.ArrayUtils;
87 import com.android.server.LocalServices;
88 import com.android.server.job.JobSchedulerInternal;
89 import com.google.android.collect.Lists;
90 import com.google.android.collect.Maps;
91 
92 import com.android.internal.R;
93 import com.android.internal.app.IBatteryStats;
94 import com.android.internal.os.BackgroundThread;
95 import com.android.internal.util.IndentingPrintWriter;
96 import com.android.server.accounts.AccountManagerService;
97 import com.android.server.backup.AccountSyncSettingsBackupHelper;
98 import com.android.server.content.SyncStorageEngine.AuthorityInfo;
99 import com.android.server.content.SyncStorageEngine.EndPoint;
100 import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
101 
102 import java.io.FileDescriptor;
103 import java.io.PrintWriter;
104 import java.util.ArrayList;
105 import java.util.Arrays;
106 import java.util.Collection;
107 import java.util.Collections;
108 import java.util.Comparator;
109 import java.util.HashMap;
110 import java.util.HashSet;
111 import java.util.List;
112 import java.util.Map;
113 import java.util.Objects;
114 import java.util.Random;
115 import java.util.Set;
116 
117 /**
118  * Implementation details:
119  * All scheduled syncs will be passed on to JobScheduler as jobs
120  * (See {@link #scheduleSyncOperationH(SyncOperation, long)}. This function schedules a job
121  * with JobScheduler with appropriate delay and constraints (according to backoffs and extras).
122  * The scheduleSyncOperationH function also assigns a unique jobId to each
123  * SyncOperation.
124  *
125  * Periodic Syncs:
126  * Each periodic sync is scheduled as a periodic job. If a periodic sync fails, we create a new
127  * one off SyncOperation and set its {@link SyncOperation#sourcePeriodicId} field to the jobId of the
128  * periodic sync. We don't allow the periodic job to run while any job initiated by it is pending.
129  *
130  * Backoffs:
131  * Each {@link EndPoint} has a backoff associated with it. When a SyncOperation fails, we increase
132  * the backoff on the authority. Then we reschedule all syncs associated with that authority to
133  * run at a later time. Similarly, when a sync succeeds, backoff is cleared and all associated syncs
134  * are rescheduled. A rescheduled sync will get a new jobId.
135  *
136  * @hide
137  */
138 public class SyncManager {
139     static final String TAG = "SyncManager";
140 
141     private static final boolean DEBUG_ACCOUNT_ACCESS = false;
142 
143     /** Delay a sync due to local changes this long. In milliseconds */
144     private static final long LOCAL_SYNC_DELAY;
145 
146     static {
147         LOCAL_SYNC_DELAY =
148                 SystemProperties.getLong("sync.local_sync_delay", 30 * 1000 /* 30 seconds */);
149     }
150 
151     /**
152      * When retrying a sync for the first time use this delay. After that
153      * the retry time will double until it reached MAX_SYNC_RETRY_TIME.
154      * In milliseconds.
155      */
156     private static final long INITIAL_SYNC_RETRY_TIME_IN_MS = 30 * 1000; // 30 seconds
157 
158     /**
159      * Default the max sync retry time to this value.
160      */
161     private static final long DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS = 60 * 60; // one hour
162 
163     /**
164      * How long to wait before retrying a sync that failed due to one already being in progress.
165      */
166     private static final int DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS = 10;
167 
168     /**
169      * How often to periodically poll network traffic for an adapter performing a sync to determine
170      * whether progress is being made.
171      */
172     private static final long SYNC_MONITOR_WINDOW_LENGTH_MILLIS = 60 * 1000; // 60 seconds
173 
174     /**
175      * How many bytes must be transferred (Tx + Rx) over the period of time defined by
176      * {@link #SYNC_MONITOR_WINDOW_LENGTH_MILLIS} for the sync to be considered to be making
177      * progress.
178      */
179     private static final int SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES = 10; // 10 bytes
180 
181     /**
182      * If a previously scheduled sync becomes ready and we are low on storage, it gets
183      * pushed back for this amount of time.
184      */
185     private static final long SYNC_DELAY_ON_LOW_STORAGE = 60*60*1000;   // 1 hour
186 
187     /**
188      * If a sync becomes ready and it conflicts with an already running sync, it gets
189      * pushed back for this amount of time.
190      */
191     private static final long SYNC_DELAY_ON_CONFLICT = 10*1000; // 10 seconds
192 
193     /**
194      * Generate job ids in the range [MIN_SYNC_JOB_ID, MAX_SYNC_JOB_ID) to avoid conflicts with
195      * other jobs scheduled by the system process.
196      */
197     private static final int MIN_SYNC_JOB_ID = 100000;
198     private static final int MAX_SYNC_JOB_ID = 110000;
199 
200     private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
201     private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
202     private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
203 
204 
205     private static final int SYNC_OP_STATE_VALID = 0;
206     private static final int SYNC_OP_STATE_INVALID = 1;
207     private static final int SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS = 2;
208 
209     private Context mContext;
210 
211     private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
212 
213     // TODO: add better locking around mRunningAccounts
214     private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY;
215 
216     volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
217     volatile private PowerManager.WakeLock mSyncManagerWakeLock;
218     volatile private boolean mDataConnectionIsConnected = false;
219     volatile private boolean mStorageIsLow = false;
220     volatile private boolean mDeviceIsIdle = false;
221     volatile private boolean mReportedSyncActive = false;
222 
223     private final NotificationManager mNotificationMgr;
224     private final IBatteryStats mBatteryStats;
225     private JobScheduler mJobScheduler;
226     private JobSchedulerInternal mJobSchedulerInternal;
227     private SyncJobService mSyncJobService;
228 
229     private SyncStorageEngine mSyncStorageEngine;
230 
231     protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList();
232 
233     // Synchronized on "this". Instead of using this directly one should instead call
234     // its accessor, getConnManager().
235     private ConnectivityManager mConnManagerDoNotUseDirectly;
236 
237     /** Track whether the device has already been provisioned. */
238     private volatile boolean mProvisioned;
239 
240     protected SyncAdaptersCache mSyncAdapters;
241 
242     private final Random mRand;
243 
isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs)244     private boolean isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs) {
245         for (JobInfo job: pendingJobs) {
246             if (job.getId() == jobId) {
247                 return true;
248             }
249         }
250         for (ActiveSyncContext asc: mActiveSyncContexts) {
251             if (asc.mSyncOperation.jobId == jobId) {
252                 return true;
253             }
254         }
255         return false;
256     }
257 
getUnusedJobIdH()258     private int getUnusedJobIdH() {
259         int newJobId;
260         do {
261             newJobId = MIN_SYNC_JOB_ID + mRand.nextInt(MAX_SYNC_JOB_ID - MIN_SYNC_JOB_ID);
262         } while (isJobIdInUseLockedH(newJobId,
263                 mJobSchedulerInternal.getSystemScheduledPendingJobs()));
264         return newJobId;
265     }
266 
getAllPendingSyncs()267     private List<SyncOperation> getAllPendingSyncs() {
268         verifyJobScheduler();
269         List<JobInfo> pendingJobs = mJobSchedulerInternal.getSystemScheduledPendingJobs();
270         List<SyncOperation> pendingSyncs = new ArrayList<SyncOperation>(pendingJobs.size());
271         for (JobInfo job: pendingJobs) {
272             SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
273             if (op != null) {
274                 pendingSyncs.add(op);
275             }
276         }
277         return pendingSyncs;
278     }
279 
280     private final BroadcastReceiver mStorageIntentReceiver =
281             new BroadcastReceiver() {
282                 @Override
283                 public void onReceive(Context context, Intent intent) {
284                     String action = intent.getAction();
285                     if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
286                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
287                             Slog.v(TAG, "Internal storage is low.");
288                         }
289                         mStorageIsLow = true;
290                         cancelActiveSync(
291                                 SyncStorageEngine.EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL,
292                                 null /* any sync */);
293                     } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
294                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
295                             Slog.v(TAG, "Internal storage is ok.");
296                         }
297                         mStorageIsLow = false;
298                         rescheduleSyncs(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL);
299                     }
300                 }
301             };
302 
303     private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
304         @Override
305         public void onReceive(Context context, Intent intent) {
306             mBootCompleted = true;
307             // Called because it gets all pending jobs and stores them in mScheduledSyncs cache.
308             verifyJobScheduler();
309             mSyncHandler.onBootCompleted();
310         }
311     };
312 
313     private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
314         @Override
315         public void onReceive(Context context, Intent intent) {
316             EndPoint target = new EndPoint(null, null, context.getUserId());
317             updateRunningAccounts(target /* sync targets for user */);
318         }
319     };
320 
321     private final PowerManager mPowerManager;
322 
323     private final UserManager mUserManager;
324 
325     private final AccountManager mAccountManager;
326 
327     private final AccountManagerInternal mAccountManagerInternal;
328 
329     private final PackageManagerInternal mPackageManagerInternal;
330 
getAllUsers()331     private List<UserInfo> getAllUsers() {
332         return mUserManager.getUsers();
333     }
334 
containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId)335     private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) {
336         boolean found = false;
337         for (int i = 0; i < accounts.length; i++) {
338             if (accounts[i].userId == userId
339                     && accounts[i].account.equals(account)) {
340                 found = true;
341                 break;
342             }
343         }
344         return found;
345     }
346 
347     /** target indicates endpoints that should be synced after account info is updated. */
updateRunningAccounts(EndPoint target)348     private void updateRunningAccounts(EndPoint target) {
349         if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_ACCOUNTS_UPDATED");
350         // Update accounts in handler thread.
351         Message m = mSyncHandler.obtainMessage(SyncHandler.MESSAGE_ACCOUNTS_UPDATED);
352         m.obj = target;
353         m.sendToTarget();
354     }
355 
doDatabaseCleanup()356     private void doDatabaseCleanup() {
357         for (UserInfo user : mUserManager.getUsers(true)) {
358             // Skip any partially created/removed users
359             if (user.partial) continue;
360             Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts(
361                     user.id, mContext.getOpPackageName());
362 
363             mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
364         }
365     }
366 
367     private BroadcastReceiver mConnectivityIntentReceiver =
368             new BroadcastReceiver() {
369                 @Override
370                 public void onReceive(Context context, Intent intent) {
371                     final boolean wasConnected = mDataConnectionIsConnected;
372 
373                     // Don't use the intent to figure out if network is connected, just check
374                     // ConnectivityManager directly.
375                     mDataConnectionIsConnected = readDataConnectionState();
376                     if (mDataConnectionIsConnected) {
377                         if (!wasConnected) {
378                             if (Log.isLoggable(TAG, Log.VERBOSE)) {
379                                 Slog.v(TAG, "Reconnection detected: clearing all backoffs");
380                             }
381                         }
382                         clearAllBackoffs();
383                     }
384                 }
385             };
386 
clearAllBackoffs()387     private void clearAllBackoffs() {
388         mSyncStorageEngine.clearAllBackoffsLocked();
389         rescheduleSyncs(EndPoint.USER_ALL_PROVIDER_ALL_ACCOUNTS_ALL);
390     }
391 
readDataConnectionState()392     private boolean readDataConnectionState() {
393         NetworkInfo networkInfo = getConnectivityManager().getActiveNetworkInfo();
394         return (networkInfo != null) && networkInfo.isConnected();
395     }
396 
397     private BroadcastReceiver mShutdownIntentReceiver =
398             new BroadcastReceiver() {
399                 @Override
400                 public void onReceive(Context context, Intent intent) {
401                     Log.w(TAG, "Writing sync state before shutdown...");
402                     getSyncStorageEngine().writeAllState();
403                 }
404             };
405 
406     private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
407         @Override
408         public void onReceive(Context context, Intent intent) {
409             String action = intent.getAction();
410             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
411             if (userId == UserHandle.USER_NULL) return;
412 
413             if (Intent.ACTION_USER_REMOVED.equals(action)) {
414                 onUserRemoved(userId);
415             } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
416                 onUserUnlocked(userId);
417             } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
418                 onUserStopped(userId);
419             }
420         }
421     };
422 
423     private final SyncHandler mSyncHandler;
424 
425     private volatile boolean mBootCompleted = false;
426     private volatile boolean mJobServiceReady = false;
427 
getConnectivityManager()428     private ConnectivityManager getConnectivityManager() {
429         synchronized (this) {
430             if (mConnManagerDoNotUseDirectly == null) {
431                 mConnManagerDoNotUseDirectly = (ConnectivityManager)mContext.getSystemService(
432                         Context.CONNECTIVITY_SERVICE);
433             }
434             return mConnManagerDoNotUseDirectly;
435         }
436     }
437 
438     /**
439      * Cancel all unnecessary jobs. This function will be run once after every boot.
440      */
cleanupJobs()441     private void cleanupJobs() {
442         // O(n^2) in number of jobs, so we run this on the background thread.
443         mSyncHandler.postAtFrontOfQueue(new Runnable() {
444             @Override
445             public void run() {
446                 List<SyncOperation> ops = getAllPendingSyncs();
447                 Set<String> cleanedKeys = new HashSet<String>();
448                 for (SyncOperation opx: ops) {
449                     if (cleanedKeys.contains(opx.key)) {
450                         continue;
451                     }
452                     cleanedKeys.add(opx.key);
453                     for (SyncOperation opy: ops) {
454                         if (opx == opy) {
455                             continue;
456                         }
457                         if (opx.key.equals(opy.key)) {
458                             mJobScheduler.cancel(opy.jobId);
459                         }
460                     }
461                 }
462             }
463         });
464     }
465 
verifyJobScheduler()466     private synchronized void verifyJobScheduler() {
467         if (mJobScheduler != null) {
468             return;
469         }
470         if (Log.isLoggable(TAG, Log.VERBOSE)) {
471             Log.d(TAG, "initializing JobScheduler object.");
472         }
473         mJobScheduler = (JobScheduler) mContext.getSystemService(
474                 Context.JOB_SCHEDULER_SERVICE);
475         mJobSchedulerInternal = LocalServices.getService(JobSchedulerInternal.class);
476         // Get all persisted syncs from JobScheduler
477         List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();
478         for (JobInfo job : pendingJobs) {
479             SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
480             if (op != null) {
481                 if (!op.isPeriodic) {
482                     // Set the pending status of this EndPoint to true. Pending icon is
483                     // shown on the settings activity.
484                     mSyncStorageEngine.markPending(op.target, true);
485                 }
486             }
487         }
488         cleanupJobs();
489     }
490 
getJobScheduler()491     private JobScheduler getJobScheduler() {
492         verifyJobScheduler();
493         return mJobScheduler;
494     }
495 
496     /**
497      * Should only be created after {@link ContentService#systemReady()} so that
498      * {@link PackageManager} is ready to query.
499      */
SyncManager(Context context, boolean factoryTest)500     public SyncManager(Context context, boolean factoryTest) {
501         // Initialize the SyncStorageEngine first, before registering observers
502         // and creating threads and so on; it may fail if the disk is full.
503         mContext = context;
504 
505         SyncStorageEngine.init(context);
506         mSyncStorageEngine = SyncStorageEngine.getSingleton();
507         mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
508             @Override
509             public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras) {
510                 scheduleSync(info.account, info.userId, reason, info.provider, extras,
511                         AuthorityInfo.UNDEFINED);
512             }
513         });
514 
515         mSyncStorageEngine.setPeriodicSyncAddedListener(
516                 new SyncStorageEngine.PeriodicSyncAddedListener() {
517                     @Override
518                     public void onPeriodicSyncAdded(EndPoint target, Bundle extras, long pollFrequency,
519                             long flex) {
520                         updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
521                     }
522                 });
523 
524         mSyncStorageEngine.setOnAuthorityRemovedListener(new SyncStorageEngine.OnAuthorityRemovedListener() {
525             @Override
526             public void onAuthorityRemoved(EndPoint removedAuthority) {
527                 removeSyncsForAuthority(removedAuthority);
528             }
529         });
530 
531         mSyncAdapters = new SyncAdaptersCache(mContext);
532 
533         mSyncHandler = new SyncHandler(BackgroundThread.get().getLooper());
534 
535         mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
536             @Override
537             public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) {
538                 if (!removed) {
539                     scheduleSync(null, UserHandle.USER_ALL,
540                             SyncOperation.REASON_SERVICE_CHANGED,
541                             type.authority, null, AuthorityInfo.UNDEFINED);
542                 }
543             }
544         }, mSyncHandler);
545 
546         mRand = new Random(System.currentTimeMillis());
547 
548         IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
549         context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
550 
551         if (!factoryTest) {
552             intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
553             intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
554             context.registerReceiver(mBootCompletedReceiver, intentFilter);
555         }
556 
557         intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
558         intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
559         context.registerReceiver(mStorageIntentReceiver, intentFilter);
560 
561         intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
562         intentFilter.setPriority(100);
563         context.registerReceiver(mShutdownIntentReceiver, intentFilter);
564 
565         intentFilter = new IntentFilter();
566         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
567         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
568         intentFilter.addAction(Intent.ACTION_USER_STOPPED);
569         mContext.registerReceiverAsUser(
570                 mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
571 
572         if (!factoryTest) {
573             mNotificationMgr = (NotificationManager)
574                     context.getSystemService(Context.NOTIFICATION_SERVICE);
575         } else {
576             mNotificationMgr = null;
577         }
578         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
579         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
580         mAccountManager = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE);
581         mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class);
582         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
583 
584         mAccountManagerInternal.addOnAppPermissionChangeListener((Account account, int uid) -> {
585             // If the UID gained access to the account kick-off syncs lacking account access
586             if (mAccountManagerInternal.hasAccountAccess(account, uid)) {
587                 scheduleSync(account, UserHandle.getUserId(uid),
588                         SyncOperation.REASON_ACCOUNTS_UPDATED,
589                         null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS);
590             }
591         });
592 
593         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
594                 BatteryStats.SERVICE_NAME));
595 
596         // This WakeLock is used to ensure that we stay awake between the time that we receive
597         // a sync alarm notification and when we finish processing it. We need to do this
598         // because we don't do the work in the alarm handler, rather we do it in a message
599         // handler.
600         mHandleAlarmWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
601                 HANDLE_SYNC_ALARM_WAKE_LOCK);
602         mHandleAlarmWakeLock.setReferenceCounted(false);
603 
604         // This WakeLock is used to ensure that we stay awake while running the sync loop
605         // message handler. Normally we will hold a sync adapter wake lock while it is being
606         // synced but during the execution of the sync loop it might finish a sync for
607         // one sync adapter before starting the sync for the other sync adapter and we
608         // don't want the device to go to sleep during that window.
609         mSyncManagerWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
610                 SYNC_LOOP_WAKE_LOCK);
611         mSyncManagerWakeLock.setReferenceCounted(false);
612 
613         mProvisioned = isDeviceProvisioned();
614         if (!mProvisioned) {
615             final ContentResolver resolver = context.getContentResolver();
616             ContentObserver provisionedObserver =
617                     new ContentObserver(null /* current thread */) {
618                         public void onChange(boolean selfChange) {
619                             mProvisioned |= isDeviceProvisioned();
620                             if (mProvisioned) {
621                                 mSyncHandler.onDeviceProvisioned();
622                                 resolver.unregisterContentObserver(this);
623                             }
624                         }
625                     };
626 
627             synchronized (mSyncHandler) {
628                 resolver.registerContentObserver(
629                         Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
630                         false /* notifyForDescendents */,
631                         provisionedObserver);
632 
633                 // The device *may* have been provisioned while we were registering above observer.
634                 // Check again to make sure.
635                 mProvisioned |= isDeviceProvisioned();
636                 if (mProvisioned) {
637                     resolver.unregisterContentObserver(provisionedObserver);
638                 }
639             }
640         }
641 
642         if (!factoryTest) {
643             // Register for account list updates for all users
644             mContext.registerReceiverAsUser(mAccountsUpdatedReceiver,
645                     UserHandle.ALL,
646                     new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION),
647                     null, null);
648         }
649 
650         // Set up the communication channel between the scheduled job and the sync manager.
651         // This is posted to the *main* looper intentionally, to defer calling startService()
652         // until after the lengthy primary boot sequence completes on that thread, to avoid
653         // spurious ANR triggering.
654         final Intent startServiceIntent = new Intent(mContext, SyncJobService.class);
655         startServiceIntent.putExtra(SyncJobService.EXTRA_MESSENGER, new Messenger(mSyncHandler));
656         new Handler(mContext.getMainLooper()).post(new Runnable() {
657             @Override
658             public void run() {
659                 mContext.startService(startServiceIntent);
660             }
661         });
662 
663         // Sync adapters were able to access the synced account without the accounts
664         // permission which circumvents our permission model. Therefore, we require
665         // sync adapters that don't have access to the account to get user consent.
666         // This can be noisy, therefore we will white-list sync adapters installed
667         // before we started checking for account access because they already know
668         // the account (they run before) which is the genie is out of the bottle.
669         whiteListExistingSyncAdaptersIfNeeded();
670     }
671 
whiteListExistingSyncAdaptersIfNeeded()672     private void whiteListExistingSyncAdaptersIfNeeded() {
673         if (!mSyncStorageEngine.shouldGrantSyncAdaptersAccountAccess()) {
674             return;
675         }
676         List<UserInfo> users = mUserManager.getUsers(true);
677         final int userCount = users.size();
678         for (int i = 0; i < userCount; i++) {
679             UserHandle userHandle = users.get(i).getUserHandle();
680             final int userId = userHandle.getIdentifier();
681             for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> service
682                     : mSyncAdapters.getAllServices(userId)) {
683                 String packageName = service.componentName.getPackageName();
684                 for (Account account : mAccountManager.getAccountsByTypeAsUser(
685                         service.type.accountType, userHandle)) {
686                     if (!canAccessAccount(account, packageName, userId)) {
687                         mAccountManager.updateAppPermission(account,
688                                 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, service.uid, true);
689                     }
690                 }
691             }
692         }
693     }
694 
isDeviceProvisioned()695     private boolean isDeviceProvisioned() {
696         final ContentResolver resolver = mContext.getContentResolver();
697         return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
698     }
699     /**
700      * Return a random value v that satisfies minValue <= v < maxValue. The difference between
701      * maxValue and minValue must be less than Integer.MAX_VALUE.
702      */
jitterize(long minValue, long maxValue)703     private long jitterize(long minValue, long maxValue) {
704         Random random = new Random(SystemClock.elapsedRealtime());
705         long spread = maxValue - minValue;
706         if (spread > Integer.MAX_VALUE) {
707             throw new IllegalArgumentException("the difference between the maxValue and the "
708                     + "minValue must be less than " + Integer.MAX_VALUE);
709         }
710         return minValue + random.nextInt((int)spread);
711     }
712 
getSyncStorageEngine()713     public SyncStorageEngine getSyncStorageEngine() {
714         return mSyncStorageEngine;
715     }
716 
getIsSyncable(Account account, int userId, String providerName)717     private int getIsSyncable(Account account, int userId, String providerName) {
718         int isSyncable = mSyncStorageEngine.getIsSyncable(account, userId, providerName);
719         UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId);
720 
721         // If it's not a restricted user, return isSyncable.
722         if (userInfo == null || !userInfo.isRestricted()) return isSyncable;
723 
724         // Else check if the sync adapter has opted-in or not.
725         RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
726                 mSyncAdapters.getServiceInfo(
727                         SyncAdapterType.newKey(providerName, account.type), userId);
728         if (syncAdapterInfo == null) return AuthorityInfo.NOT_SYNCABLE;
729 
730         PackageInfo pInfo = null;
731         try {
732             pInfo = AppGlobals.getPackageManager().getPackageInfo(
733                     syncAdapterInfo.componentName.getPackageName(), 0, userId);
734             if (pInfo == null) return AuthorityInfo.NOT_SYNCABLE;
735         } catch (RemoteException re) {
736             // Shouldn't happen.
737             return AuthorityInfo.NOT_SYNCABLE;
738         }
739         if (pInfo.restrictedAccountType != null
740                 && pInfo.restrictedAccountType.equals(account.type)) {
741             return isSyncable;
742         } else {
743             return AuthorityInfo.NOT_SYNCABLE;
744         }
745     }
746 
setAuthorityPendingState(EndPoint info)747     private void setAuthorityPendingState(EndPoint info) {
748         List<SyncOperation> ops = getAllPendingSyncs();
749         for (SyncOperation op: ops) {
750             if (!op.isPeriodic && op.target.matchesSpec(info)) {
751                 getSyncStorageEngine().markPending(info, true);
752                 return;
753             }
754         }
755         getSyncStorageEngine().markPending(info, false);
756     }
757 
758     /**
759      * Initiate a sync. This can start a sync for all providers
760      * (pass null to url, set onlyTicklable to false), only those
761      * providers that are marked as ticklable (pass null to url,
762      * set onlyTicklable to true), or a specific provider (set url
763      * to the content url of the provider).
764      *
765      * <p>If the ContentResolver.SYNC_EXTRAS_UPLOAD boolean in extras is
766      * true then initiate a sync that just checks for local changes to send
767      * to the server, otherwise initiate a sync that first gets any
768      * changes from the server before sending local changes back to
769      * the server.
770      *
771      * <p>If a specific provider is being synced (the url is non-null)
772      * then the extras can contain SyncAdapter-specific information
773      * to control what gets synced (e.g. which specific feed to sync).
774      *
775      * <p>You'll start getting callbacks after this.
776      *
777      * @param requestedAccount the account to sync, may be null to signify all accounts
778      * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL,
779      *          then all users' accounts are considered.
780      * @param reason for sync request. If this is a positive integer, it is the Linux uid
781      * assigned to the process that requested the sync. If it's negative, the sync was requested by
782      * the SyncManager itself and could be one of the following:
783      *      {@link SyncOperation#REASON_BACKGROUND_DATA_SETTINGS_CHANGED}
784      *      {@link SyncOperation#REASON_ACCOUNTS_UPDATED}
785      *      {@link SyncOperation#REASON_SERVICE_CHANGED}
786      *      {@link SyncOperation#REASON_PERIODIC}
787      *      {@link SyncOperation#REASON_IS_SYNCABLE}
788      *      {@link SyncOperation#REASON_SYNC_AUTO}
789      *      {@link SyncOperation#REASON_MASTER_SYNC_AUTO}
790      *      {@link SyncOperation#REASON_USER_START}
791      * @param requestedAuthority the authority to sync, may be null to indicate all authorities
792      * @param extras a Map of SyncAdapter-specific information to control
793      *          syncs of a specific provider. Can be null. Is ignored
794      *          if the url is null.
795      * @param targetSyncState Only sync authorities that have the specified sync state.
796      *           Use {@link AuthorityInfo#UNDEFINED} to sync all authorities.
797      */
scheduleSync(Account requestedAccount, int userId, int reason, String requestedAuthority, Bundle extras, int targetSyncState)798     public void scheduleSync(Account requestedAccount, int userId, int reason,
799                              String requestedAuthority, Bundle extras, int targetSyncState) {
800         scheduleSync(requestedAccount, userId, reason, requestedAuthority, extras, targetSyncState,
801                 0 /* min delay */);
802     }
803 
804     /**
805      * @param minDelayMillis The sync can't land before this delay expires.
806      */
scheduleSync(Account requestedAccount, int userId, int reason, String requestedAuthority, Bundle extras, int targetSyncState, final long minDelayMillis)807     private void scheduleSync(Account requestedAccount, int userId, int reason,
808                              String requestedAuthority, Bundle extras, int targetSyncState,
809                              final long minDelayMillis) {
810         final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
811         if (extras == null) {
812             extras = new Bundle();
813         }
814         if (isLoggable) {
815             Log.d(TAG, "one-time sync for: " + requestedAccount + " " + extras.toString() + " "
816                     + requestedAuthority);
817         }
818 
819         AccountAndUser[] accounts = null;
820         if (requestedAccount != null) {
821             if (userId != UserHandle.USER_ALL) {
822                 accounts = new AccountAndUser[]{new AccountAndUser(requestedAccount, userId)};
823             } else {
824                 for (AccountAndUser runningAccount : mRunningAccounts) {
825                     if (requestedAccount.equals(runningAccount.account)) {
826                         accounts = ArrayUtils.appendElement(AccountAndUser.class,
827                                 accounts, runningAccount);
828                     }
829                 }
830             }
831         } else {
832             accounts = mRunningAccounts;
833         }
834 
835         if (ArrayUtils.isEmpty(accounts)) {
836             if (isLoggable) {
837                 Slog.v(TAG, "scheduleSync: no accounts configured, dropping");
838             }
839             return;
840         }
841 
842         final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
843         final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
844         if (manualSync) {
845             extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
846             extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
847         }
848         final boolean ignoreSettings =
849                 extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
850 
851         int source;
852         if (uploadOnly) {
853             source = SyncStorageEngine.SOURCE_LOCAL;
854         } else if (manualSync) {
855             source = SyncStorageEngine.SOURCE_USER;
856         } else if (requestedAuthority == null) {
857             source = SyncStorageEngine.SOURCE_POLL;
858         } else {
859             // This isn't strictly server, since arbitrary callers can (and do) request
860             // a non-forced two-way sync on a specific url.
861             source = SyncStorageEngine.SOURCE_SERVER;
862         }
863 
864         for (AccountAndUser account : accounts) {
865             // If userId is specified, do not sync accounts of other users
866             if (userId >= UserHandle.USER_SYSTEM && account.userId >= UserHandle.USER_SYSTEM
867                     && userId != account.userId) {
868                 continue;
869             }
870             // Compile a list of authorities that have sync adapters.
871             // For each authority sync each account that matches a sync adapter.
872             final HashSet<String> syncableAuthorities = new HashSet<String>();
873             for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
874                     mSyncAdapters.getAllServices(account.userId)) {
875                 syncableAuthorities.add(syncAdapter.type.authority);
876             }
877 
878             // If the url was specified then replace the list of authorities
879             // with just this authority or clear it if this authority isn't
880             // syncable.
881             if (requestedAuthority != null) {
882                 final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
883                 syncableAuthorities.clear();
884                 if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
885             }
886 
887             for (String authority : syncableAuthorities) {
888                 int isSyncable = computeSyncable(account.account, account.userId, authority);
889 
890                 if (isSyncable == AuthorityInfo.NOT_SYNCABLE) {
891                     continue;
892                 }
893 
894                 final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
895                         mSyncAdapters.getServiceInfo(SyncAdapterType.newKey(authority,
896                                 account.account.type), account.userId);
897                 if (syncAdapterInfo == null) {
898                     continue;
899                 }
900 
901                 final int owningUid = syncAdapterInfo.uid;
902 
903                 if (isSyncable == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) {
904                     if (isLoggable) {
905                         Slog.v(TAG, "    Not scheduling sync operation: "
906                                 + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS");
907                     }
908                     Bundle finalExtras = new Bundle(extras);
909                     String packageName = syncAdapterInfo.componentName.getPackageName();
910                     // If the app did not run and has no account access, done
911                     try {
912                         if (!mPackageManagerInternal.wasPackageEverLaunched(packageName, userId)) {
913                             continue;
914                         }
915                     } catch (IllegalArgumentException e) {
916                         // Package not found, race with an uninstall
917                         continue;
918                     }
919                     mAccountManagerInternal.requestAccountAccess(account.account,
920                             packageName, userId,
921                             new RemoteCallback((Bundle result) -> {
922                                 if (result != null
923                                         && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
924                                     scheduleSync(account.account, userId, reason, authority,
925                                             finalExtras, targetSyncState, minDelayMillis);
926                                 }
927                             }
928                         ));
929                     continue;
930                 }
931 
932                 final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
933                 final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
934                 if (isSyncable < 0 && isAlwaysSyncable) {
935                     mSyncStorageEngine.setIsSyncable(
936                             account.account, account.userId, authority, AuthorityInfo.SYNCABLE);
937                     isSyncable = AuthorityInfo.SYNCABLE;
938                 }
939 
940                 if (targetSyncState != AuthorityInfo.UNDEFINED && targetSyncState != isSyncable) {
941                     continue;
942                 }
943 
944                 if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
945                     continue;
946                 }
947 
948                 boolean syncAllowed =
949                         (isSyncable < 0) // Always allow if the isSyncable state is unknown.
950                                 || ignoreSettings
951                                 || (mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
952                                 && mSyncStorageEngine.getSyncAutomatically(account.account,
953                                 account.userId, authority));
954                 if (!syncAllowed) {
955                     if (isLoggable) {
956                         Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
957                                 + " is not allowed, dropping request");
958                     }
959                     continue;
960                 }
961                 SyncStorageEngine.EndPoint info =
962                         new SyncStorageEngine.EndPoint(
963                                 account.account, authority, account.userId);
964                 long delayUntil =
965                         mSyncStorageEngine.getDelayUntilTime(info);
966 
967                 final String owningPackage = syncAdapterInfo.componentName.getPackageName();
968 
969                 if (isSyncable == AuthorityInfo.NOT_INITIALIZED) {
970                     // Initialisation sync.
971                     Bundle newExtras = new Bundle();
972                     newExtras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
973                     if (isLoggable) {
974                         Slog.v(TAG, "schedule initialisation Sync:"
975                                 + ", delay until " + delayUntil
976                                 + ", run by " + 0
977                                 + ", flexMillis " + 0
978                                 + ", source " + source
979                                 + ", account " + account
980                                 + ", authority " + authority
981                                 + ", extras " + newExtras);
982                     }
983                     postScheduleSyncMessage(
984                             new SyncOperation(account.account, account.userId,
985                                     owningUid, owningPackage, reason, source,
986                                     authority, newExtras, allowParallelSyncs),
987                             minDelayMillis
988                     );
989                 } else if (targetSyncState == AuthorityInfo.UNDEFINED
990                         || targetSyncState == isSyncable) {
991                     if (isLoggable) {
992                         Slog.v(TAG, "scheduleSync:"
993                                 + " delay until " + delayUntil
994                                 + ", source " + source
995                                 + ", account " + account
996                                 + ", authority " + authority
997                                 + ", extras " + extras);
998                     }
999                     postScheduleSyncMessage(
1000                             new SyncOperation(account.account, account.userId,
1001                                     owningUid, owningPackage, reason, source,
1002                                     authority, extras, allowParallelSyncs),
1003                             minDelayMillis
1004                     );
1005                 }
1006             }
1007         }
1008     }
1009 
computeSyncable(Account account, int userId, String authority)1010     private int computeSyncable(Account account, int userId, String authority) {
1011         return computeSyncable(account, userId, authority, true);
1012     }
1013 
computeSyncable(Account account, int userId, String authority, boolean checkAccountAccess)1014     public int computeSyncable(Account account, int userId, String authority,
1015             boolean checkAccountAccess) {
1016         final int status = getIsSyncable(account, userId, authority);
1017         if (status == AuthorityInfo.NOT_SYNCABLE) {
1018             return AuthorityInfo.NOT_SYNCABLE;
1019         }
1020         final SyncAdapterType type = SyncAdapterType.newKey(authority, account.type);
1021         final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
1022                 mSyncAdapters.getServiceInfo(type, userId);
1023         if (syncAdapterInfo == null) {
1024             return AuthorityInfo.NOT_SYNCABLE;
1025         }
1026         final int owningUid = syncAdapterInfo.uid;
1027         final String owningPackage = syncAdapterInfo.componentName.getPackageName();
1028         try {
1029             if (ActivityManager.getService().isAppStartModeDisabled(owningUid, owningPackage)) {
1030                 Slog.w(TAG, "Not scheduling job " + syncAdapterInfo.uid + ":"
1031                         + syncAdapterInfo.componentName
1032                         + " -- package not allowed to start");
1033                 return AuthorityInfo.NOT_SYNCABLE;
1034             }
1035         } catch (RemoteException e) {
1036             /* ignore - local call */
1037         }
1038         if (checkAccountAccess && !canAccessAccount(account, owningPackage, owningUid)) {
1039             Log.w(TAG, "Access to " + account + " denied for package "
1040                     + owningPackage + " in UID " + syncAdapterInfo.uid);
1041             return AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS;
1042         }
1043 
1044         return status;
1045     }
1046 
canAccessAccount(Account account, String packageName, int uid)1047     private boolean canAccessAccount(Account account, String packageName, int uid) {
1048         if (mAccountManager.hasAccountAccess(account, packageName,
1049                 UserHandle.getUserHandleForUid(uid))) {
1050             return true;
1051         }
1052         // We relax the account access rule to also include the system apps as
1053         // they are trusted and we want to minimize the cases where the user
1054         // involvement is required to grant access to the synced account.
1055         try {
1056             mContext.getPackageManager().getApplicationInfoAsUser(packageName,
1057                     PackageManager.MATCH_SYSTEM_ONLY, UserHandle.getUserId(uid));
1058             return true;
1059         } catch (NameNotFoundException e) {
1060             return false;
1061         }
1062     }
1063 
removeSyncsForAuthority(EndPoint info)1064     private void removeSyncsForAuthority(EndPoint info) {
1065         verifyJobScheduler();
1066         List<SyncOperation> ops = getAllPendingSyncs();
1067         for (SyncOperation op: ops) {
1068             if (op.target.matchesSpec(info)) {
1069                  getJobScheduler().cancel(op.jobId);
1070             }
1071         }
1072     }
1073 
1074     /**
1075      * Remove a specific periodic sync identified by its target and extras.
1076      */
removePeriodicSync(EndPoint target, Bundle extras)1077     public void removePeriodicSync(EndPoint target, Bundle extras) {
1078         Message m = mSyncHandler.obtainMessage(mSyncHandler.MESSAGE_REMOVE_PERIODIC_SYNC, target);
1079         m.setData(extras);
1080         m.sendToTarget();
1081     }
1082 
1083     /**
1084      * Add a periodic sync. If a sync with same target and extras exists, its period and
1085      * flexMillis will be updated.
1086      */
updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex, Bundle extras)1087     public void updateOrAddPeriodicSync(EndPoint target, long pollFrequency, long flex,
1088             Bundle extras) {
1089         UpdatePeriodicSyncMessagePayload payload = new UpdatePeriodicSyncMessagePayload(target,
1090                 pollFrequency, flex, extras);
1091         mSyncHandler.obtainMessage(SyncHandler.MESSAGE_UPDATE_PERIODIC_SYNC, payload)
1092                 .sendToTarget();
1093     }
1094 
1095     /**
1096      * Get a list of periodic syncs corresponding to the given target.
1097      */
getPeriodicSyncs(EndPoint target)1098     public List<PeriodicSync> getPeriodicSyncs(EndPoint target) {
1099         List<SyncOperation> ops = getAllPendingSyncs();
1100         List<PeriodicSync> periodicSyncs = new ArrayList<PeriodicSync>();
1101 
1102         for (SyncOperation op: ops) {
1103             if (op.isPeriodic && op.target.matchesSpec(target)) {
1104                 periodicSyncs.add(new PeriodicSync(op.target.account, op.target.provider,
1105                         op.extras, op.periodMillis / 1000, op.flexMillis / 1000));
1106             }
1107         }
1108 
1109         return periodicSyncs;
1110     }
1111 
1112     /**
1113      * Schedule sync based on local changes to a provider. We wait for at least LOCAL_SYNC_DELAY
1114      * ms to batch syncs.
1115      */
scheduleLocalSync(Account account, int userId, int reason, String authority)1116     public void scheduleLocalSync(Account account, int userId, int reason, String authority) {
1117         final Bundle extras = new Bundle();
1118         extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
1119         scheduleSync(account, userId, reason, authority, extras,
1120                 AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY);
1121     }
1122 
getSyncAdapterTypes(int userId)1123     public SyncAdapterType[] getSyncAdapterTypes(int userId) {
1124         final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
1125         serviceInfos = mSyncAdapters.getAllServices(userId);
1126         SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
1127         int i = 0;
1128         for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
1129             types[i] = serviceInfo.type;
1130             ++i;
1131         }
1132         return types;
1133     }
1134 
getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId)1135     public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
1136         return mSyncAdapters.getSyncAdapterPackagesForAuthority(authority, userId);
1137     }
1138 
sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext, SyncResult syncResult)1139     private void sendSyncFinishedOrCanceledMessage(ActiveSyncContext syncContext,
1140             SyncResult syncResult) {
1141         if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_SYNC_FINISHED");
1142         Message msg = mSyncHandler.obtainMessage();
1143         msg.what = SyncHandler.MESSAGE_SYNC_FINISHED;
1144         msg.obj = new SyncFinishedOrCancelledMessagePayload(syncContext, syncResult);
1145         mSyncHandler.sendMessage(msg);
1146     }
1147 
sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras)1148     private void sendCancelSyncsMessage(final SyncStorageEngine.EndPoint info, Bundle extras) {
1149         if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "sending MESSAGE_CANCEL");
1150         Message msg = mSyncHandler.obtainMessage();
1151         msg.what = SyncHandler.MESSAGE_CANCEL;
1152         msg.setData(extras);
1153         msg.obj = info;
1154         mSyncHandler.sendMessage(msg);
1155     }
1156 
1157     /**
1158      * Post a delayed message that will monitor the given sync context by periodically checking how
1159      * much network has been used by the uid.
1160      */
postMonitorSyncProgressMessage(ActiveSyncContext activeSyncContext)1161     private void postMonitorSyncProgressMessage(ActiveSyncContext activeSyncContext) {
1162         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1163             Slog.v(TAG, "posting MESSAGE_SYNC_MONITOR in " +
1164                     (SYNC_MONITOR_WINDOW_LENGTH_MILLIS/1000) + "s");
1165         }
1166 
1167         activeSyncContext.mBytesTransferredAtLastPoll =
1168                 getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid);
1169         activeSyncContext.mLastPolledTimeElapsed = SystemClock.elapsedRealtime();
1170         Message monitorMessage =
1171                 mSyncHandler.obtainMessage(
1172                         SyncHandler.MESSAGE_MONITOR_SYNC,
1173                         activeSyncContext);
1174         mSyncHandler.sendMessageDelayed(monitorMessage, SYNC_MONITOR_WINDOW_LENGTH_MILLIS);
1175     }
1176 
postScheduleSyncMessage(SyncOperation syncOperation, long minDelayMillis)1177     private void postScheduleSyncMessage(SyncOperation syncOperation, long minDelayMillis) {
1178         ScheduleSyncMessagePayload payload =
1179                 new ScheduleSyncMessagePayload(syncOperation, minDelayMillis);
1180         mSyncHandler.obtainMessage(mSyncHandler.MESSAGE_SCHEDULE_SYNC, payload).sendToTarget();
1181     }
1182 
1183     /**
1184      * Monitor sync progress by calculating how many bytes it is managing to send to and fro.
1185      */
getTotalBytesTransferredByUid(int uid)1186     private long getTotalBytesTransferredByUid(int uid) {
1187         return (TrafficStats.getUidRxBytes(uid) + TrafficStats.getUidTxBytes(uid));
1188     }
1189 
1190     /**
1191      * Convenience class for passing parameters for a finished or cancelled sync to the handler
1192      * to be processed.
1193      */
1194     private class SyncFinishedOrCancelledMessagePayload {
1195         public final ActiveSyncContext activeSyncContext;
1196         public final SyncResult syncResult;
1197 
SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext, SyncResult syncResult)1198         SyncFinishedOrCancelledMessagePayload(ActiveSyncContext syncContext,
1199                 SyncResult syncResult) {
1200             this.activeSyncContext = syncContext;
1201             this.syncResult = syncResult;
1202         }
1203     }
1204 
1205     private class UpdatePeriodicSyncMessagePayload {
1206         public final EndPoint target;
1207         public final long pollFrequency;
1208         public final long flex;
1209         public final Bundle extras;
1210 
UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex, Bundle extras)1211         UpdatePeriodicSyncMessagePayload(EndPoint target, long pollFrequency, long flex,
1212                 Bundle extras) {
1213             this.target = target;
1214             this.pollFrequency = pollFrequency;
1215             this.flex = flex;
1216             this.extras = extras;
1217         }
1218     }
1219 
1220     private static class ScheduleSyncMessagePayload {
1221         final SyncOperation syncOperation;
1222         final long minDelayMillis;
1223 
ScheduleSyncMessagePayload(SyncOperation syncOperation, long minDelayMillis)1224         ScheduleSyncMessagePayload(SyncOperation syncOperation, long minDelayMillis) {
1225             this.syncOperation = syncOperation;
1226             this.minDelayMillis = minDelayMillis;
1227         }
1228     }
1229 
clearBackoffSetting(EndPoint target)1230     private void clearBackoffSetting(EndPoint target) {
1231         Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(target);
1232         if (backoff != null && backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE &&
1233                 backoff.second == SyncStorageEngine.NOT_IN_BACKOFF_MODE) {
1234             return;
1235         }
1236         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1237             Slog.v(TAG, "Clearing backoffs for " + target);
1238         }
1239         mSyncStorageEngine.setBackoff(target,
1240                 SyncStorageEngine.NOT_IN_BACKOFF_MODE,
1241                 SyncStorageEngine.NOT_IN_BACKOFF_MODE);
1242 
1243         rescheduleSyncs(target);
1244     }
1245 
increaseBackoffSetting(EndPoint target)1246     private void increaseBackoffSetting(EndPoint target) {
1247         final long now = SystemClock.elapsedRealtime();
1248 
1249         final Pair<Long, Long> previousSettings =
1250                 mSyncStorageEngine.getBackoff(target);
1251         long newDelayInMs = -1;
1252         if (previousSettings != null) {
1253             // Don't increase backoff before current backoff is expired. This will happen for op's
1254             // with ignoreBackoff set.
1255             if (now < previousSettings.first) {
1256                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1257                     Slog.v(TAG, "Still in backoff, do not increase it. "
1258                             + "Remaining: " + ((previousSettings.first - now) / 1000) + " seconds.");
1259                 }
1260                 return;
1261             }
1262             // Subsequent delays are the double of the previous delay.
1263             newDelayInMs = previousSettings.second * 2;
1264         }
1265         if (newDelayInMs <= 0) {
1266             // The initial delay is the jitterized INITIAL_SYNC_RETRY_TIME_IN_MS.
1267             newDelayInMs = jitterize(INITIAL_SYNC_RETRY_TIME_IN_MS,
1268                     (long)(INITIAL_SYNC_RETRY_TIME_IN_MS * 1.1));
1269         }
1270 
1271         // Cap the delay.
1272         long maxSyncRetryTimeInSeconds = Settings.Global.getLong(mContext.getContentResolver(),
1273                 Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
1274                 DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS);
1275         if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
1276             newDelayInMs = maxSyncRetryTimeInSeconds * 1000;
1277         }
1278 
1279         final long backoff = now + newDelayInMs;
1280         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1281             Slog.v(TAG, "Backoff until: " + backoff + ", delayTime: " + newDelayInMs);
1282         }
1283         mSyncStorageEngine.setBackoff(target, backoff, newDelayInMs);
1284         rescheduleSyncs(target);
1285     }
1286 
1287     /**
1288      * Reschedule all scheduled syncs for this EndPoint. The syncs will be scheduled according
1289      * to current backoff and delayUntil values of this EndPoint.
1290      */
rescheduleSyncs(EndPoint target)1291     private void rescheduleSyncs(EndPoint target) {
1292         List<SyncOperation> ops = getAllPendingSyncs();
1293         int count = 0;
1294         for (SyncOperation op: ops) {
1295             if (!op.isPeriodic && op.target.matchesSpec(target)) {
1296                 count++;
1297                 getJobScheduler().cancel(op.jobId);
1298                 postScheduleSyncMessage(op, 0 /* min delay */);
1299             }
1300         }
1301         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1302             Slog.v(TAG, "Rescheduled " + count + " syncs for " + target);
1303         }
1304     }
1305 
setDelayUntilTime(EndPoint target, long delayUntilSeconds)1306     private void setDelayUntilTime(EndPoint target, long delayUntilSeconds) {
1307         final long delayUntil = delayUntilSeconds * 1000;
1308         final long absoluteNow = System.currentTimeMillis();
1309         long newDelayUntilTime;
1310         if (delayUntil > absoluteNow) {
1311             newDelayUntilTime = SystemClock.elapsedRealtime() + (delayUntil - absoluteNow);
1312         } else {
1313             newDelayUntilTime = 0;
1314         }
1315         mSyncStorageEngine.setDelayUntilTime(target, newDelayUntilTime);
1316         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1317             Slog.v(TAG, "Delay Until time set to " + newDelayUntilTime + " for " + target);
1318         }
1319         rescheduleSyncs(target);
1320     }
1321 
isAdapterDelayed(EndPoint target)1322     private boolean isAdapterDelayed(EndPoint target) {
1323         long now = SystemClock.elapsedRealtime();
1324         Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(target);
1325         if (backoff != null && backoff.first != SyncStorageEngine.NOT_IN_BACKOFF_MODE
1326                 && backoff.first > now) {
1327             return true;
1328         }
1329         if (mSyncStorageEngine.getDelayUntilTime(target) > now) {
1330             return true;
1331         }
1332         return false;
1333     }
1334 
1335     /**
1336      * Cancel the active sync if it matches the target.
1337      * @param info object containing info about which syncs to cancel. The target can
1338      * have null account/provider info to specify all accounts/providers.
1339      * @param extras if non-null, specifies the exact sync to remove.
1340      */
cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras)1341     public void cancelActiveSync(SyncStorageEngine.EndPoint info, Bundle extras) {
1342         sendCancelSyncsMessage(info, extras);
1343     }
1344 
1345     /**
1346      * Schedule a sync operation with JobScheduler.
1347      */
scheduleSyncOperationH(SyncOperation syncOperation)1348     private void scheduleSyncOperationH(SyncOperation syncOperation) {
1349         scheduleSyncOperationH(syncOperation, 0L);
1350     }
1351 
scheduleSyncOperationH(SyncOperation syncOperation, long minDelay)1352     private void scheduleSyncOperationH(SyncOperation syncOperation, long minDelay) {
1353         final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
1354         if (syncOperation == null) {
1355             Slog.e(TAG, "Can't schedule null sync operation.");
1356             return;
1357         }
1358         if (!syncOperation.ignoreBackoff()) {
1359             Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(syncOperation.target);
1360             if (backoff == null) {
1361                 Slog.e(TAG, "Couldn't find backoff values for " + syncOperation.target);
1362                 backoff = new Pair<Long, Long>(SyncStorageEngine.NOT_IN_BACKOFF_MODE,
1363                         SyncStorageEngine.NOT_IN_BACKOFF_MODE);
1364             }
1365             long now = SystemClock.elapsedRealtime();
1366             long backoffDelay = backoff.first == SyncStorageEngine.NOT_IN_BACKOFF_MODE ? 0
1367                     : backoff.first - now;
1368             long delayUntil = mSyncStorageEngine.getDelayUntilTime(syncOperation.target);
1369             long delayUntilDelay = delayUntil > now ? delayUntil - now : 0;
1370             if (isLoggable) {
1371                 Slog.v(TAG, "backoff delay:" + backoffDelay
1372                         + " delayUntil delay:" + delayUntilDelay);
1373             }
1374             minDelay = Math.max(minDelay, Math.max(backoffDelay, delayUntilDelay));
1375         }
1376 
1377         if (minDelay < 0) {
1378             minDelay = 0;
1379         }
1380 
1381         // Check if duplicate syncs are pending. If found, keep one with least expected run time.
1382         if (!syncOperation.isPeriodic) {
1383             // Check currently running syncs
1384             for (ActiveSyncContext asc: mActiveSyncContexts) {
1385                 if (asc.mSyncOperation.key.equals(syncOperation.key)) {
1386                     if (isLoggable) {
1387                         Log.v(TAG, "Duplicate sync is already running. Not scheduling "
1388                                 + syncOperation);
1389                     }
1390                     return;
1391                 }
1392             }
1393 
1394             int duplicatesCount = 0;
1395             long now = SystemClock.elapsedRealtime();
1396             syncOperation.expectedRuntime = now + minDelay;
1397             List<SyncOperation> pending = getAllPendingSyncs();
1398             SyncOperation opWithLeastExpectedRuntime = syncOperation;
1399             for (SyncOperation op : pending) {
1400                 if (op.isPeriodic) {
1401                     continue;
1402                 }
1403                 if (op.key.equals(syncOperation.key)) {
1404                     if (opWithLeastExpectedRuntime.expectedRuntime > op.expectedRuntime) {
1405                         opWithLeastExpectedRuntime = op;
1406                     }
1407                     duplicatesCount++;
1408                 }
1409             }
1410             if (duplicatesCount > 1) {
1411                 Slog.e(TAG, "FATAL ERROR! File a bug if you see this.");
1412             }
1413             for (SyncOperation op : pending) {
1414                 if (op.isPeriodic) {
1415                     continue;
1416                 }
1417                 if (op.key.equals(syncOperation.key)) {
1418                     if (op != opWithLeastExpectedRuntime) {
1419                         if (isLoggable) {
1420                             Slog.v(TAG, "Cancelling duplicate sync " + op);
1421                         }
1422                         getJobScheduler().cancel(op.jobId);
1423                     }
1424                 }
1425             }
1426             if (opWithLeastExpectedRuntime != syncOperation) {
1427                 // Don't schedule because a duplicate sync with earlier expected runtime exists.
1428                 if (isLoggable) {
1429                     Slog.v(TAG, "Not scheduling because a duplicate exists.");
1430                 }
1431                 return;
1432             }
1433         }
1434 
1435         // Syncs that are re-scheduled shouldn't get a new job id.
1436         if (syncOperation.jobId == SyncOperation.NO_JOB_ID) {
1437             syncOperation.jobId = getUnusedJobIdH();
1438         }
1439 
1440         if (isLoggable) {
1441             Slog.v(TAG, "scheduling sync operation " + syncOperation.toString());
1442         }
1443 
1444         int priority = syncOperation.findPriority();
1445 
1446         final int networkType = syncOperation.isNotAllowedOnMetered() ?
1447                 JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY;
1448 
1449         JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
1450                 new ComponentName(mContext, SyncJobService.class))
1451                 .setExtras(syncOperation.toJobInfoExtras())
1452                 .setRequiredNetworkType(networkType)
1453                 .setPersisted(true)
1454                 .setPriority(priority);
1455 
1456         if (syncOperation.isPeriodic) {
1457             b.setPeriodic(syncOperation.periodMillis, syncOperation.flexMillis);
1458         } else {
1459             if (minDelay > 0) {
1460                 b.setMinimumLatency(minDelay);
1461             }
1462             getSyncStorageEngine().markPending(syncOperation.target, true);
1463         }
1464 
1465         if (syncOperation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_REQUIRE_CHARGING)) {
1466             b.setRequiresCharging(true);
1467         }
1468 
1469         getJobScheduler().scheduleAsPackage(b.build(), syncOperation.owningPackage,
1470                 syncOperation.target.userId, syncOperation.wakeLockName());
1471     }
1472 
1473     /**
1474      * Remove scheduled sync operations.
1475      * @param info limit the removals to operations that match this target. The target can
1476      * have null account/provider info to specify all accounts/providers.
1477      */
clearScheduledSyncOperations(SyncStorageEngine.EndPoint info)1478     public void clearScheduledSyncOperations(SyncStorageEngine.EndPoint info) {
1479         List<SyncOperation> ops = getAllPendingSyncs();
1480         for (SyncOperation op: ops) {
1481             if (!op.isPeriodic && op.target.matchesSpec(info)) {
1482                 getJobScheduler().cancel(op.jobId);
1483                 getSyncStorageEngine().markPending(op.target, false);
1484             }
1485         }
1486         mSyncStorageEngine.setBackoff(info,
1487                 SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
1488     }
1489 
1490     /**
1491      * Remove a specified sync, if it exists.
1492      * @param info Authority for which the sync is to be removed.
1493      * @param extras extras bundle to uniquely identify sync.
1494      */
cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras)1495     public void cancelScheduledSyncOperation(SyncStorageEngine.EndPoint info, Bundle extras) {
1496         List<SyncOperation> ops = getAllPendingSyncs();
1497         for (SyncOperation op: ops) {
1498             if (!op.isPeriodic && op.target.matchesSpec(info)
1499                     && syncExtrasEquals(extras, op.extras, false)) {
1500                 getJobScheduler().cancel(op.jobId);
1501             }
1502         }
1503         setAuthorityPendingState(info);
1504         // Reset the back-off if there are no more syncs pending.
1505         if (!mSyncStorageEngine.isSyncPending(info)) {
1506             mSyncStorageEngine.setBackoff(info,
1507                     SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
1508         }
1509     }
1510 
maybeRescheduleSync(SyncResult syncResult, SyncOperation operation)1511     private void maybeRescheduleSync(SyncResult syncResult, SyncOperation operation) {
1512         final boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
1513         if (isLoggable) {
1514             Log.d(TAG, "encountered error(s) during the sync: " + syncResult + ", " + operation);
1515         }
1516 
1517         // The SYNC_EXTRAS_IGNORE_BACKOFF only applies to the first attempt to sync a given
1518         // request. Retries of the request will always honor the backoff, so clear the
1519         // flag in case we retry this request.
1520         if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, false)) {
1521             operation.extras.remove(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF);
1522         }
1523 
1524         if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false)
1525                 && !syncResult.syncAlreadyInProgress) {
1526             // syncAlreadyInProgress flag is set by AbstractThreadedSyncAdapter. The sync adapter
1527             // has no way of knowing that a sync error occured. So we DO retry if the error is
1528             // syncAlreadyInProgress.
1529             if (isLoggable) {
1530                 Log.d(TAG, "not retrying sync operation because SYNC_EXTRAS_DO_NOT_RETRY was specified "
1531                         + operation);
1532             }
1533         } else if (operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false)
1534                 && !syncResult.syncAlreadyInProgress) {
1535             // If this was an upward sync then schedule a two-way sync immediately.
1536             operation.extras.remove(ContentResolver.SYNC_EXTRAS_UPLOAD);
1537             if (isLoggable) {
1538                 Log.d(TAG, "retrying sync operation as a two-way sync because an upload-only sync "
1539                         + "encountered an error: " + operation);
1540             }
1541             scheduleSyncOperationH(operation);
1542         } else if (syncResult.tooManyRetries) {
1543             // If this sync aborted because the internal sync loop retried too many times then
1544             //   don't reschedule. Otherwise we risk getting into a retry loop.
1545             if (isLoggable) {
1546                 Log.d(TAG, "not retrying sync operation because it retried too many times: "
1547                         + operation);
1548             }
1549         } else if (syncResult.madeSomeProgress()) {
1550             // If the operation succeeded to some extent then retry immediately.
1551             if (isLoggable) {
1552                 Log.d(TAG, "retrying sync operation because even though it had an error "
1553                         + "it achieved some success");
1554             }
1555             scheduleSyncOperationH(operation);
1556         } else if (syncResult.syncAlreadyInProgress) {
1557             if (isLoggable) {
1558                 Log.d(TAG, "retrying sync operation that failed because there was already a "
1559                         + "sync in progress: " + operation);
1560             }
1561             scheduleSyncOperationH(operation, DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000);
1562         } else if (syncResult.hasSoftError()) {
1563             // If this was a two-way sync then retry soft errors with an exponential backoff.
1564             if (isLoggable) {
1565                 Log.d(TAG, "retrying sync operation because it encountered a soft error: "
1566                         + operation);
1567             }
1568             scheduleSyncOperationH(operation);
1569         } else {
1570             // Otherwise do not reschedule.
1571             Log.d(TAG, "not retrying sync operation because the error is a hard error: "
1572                     + operation);
1573         }
1574     }
1575 
onUserUnlocked(int userId)1576     private void onUserUnlocked(int userId) {
1577         // Make sure that accounts we're about to use are valid.
1578         AccountManagerService.getSingleton().validateAccounts(userId);
1579 
1580         mSyncAdapters.invalidateCache(userId);
1581 
1582         EndPoint target = new EndPoint(null, null, userId);
1583         updateRunningAccounts(target);
1584 
1585         // Schedule sync for any accounts under started user.
1586         final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId,
1587                 mContext.getOpPackageName());
1588         for (Account account : accounts) {
1589             scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
1590                     AuthorityInfo.NOT_INITIALIZED);
1591         }
1592     }
1593 
onUserStopped(int userId)1594     private void onUserStopped(int userId) {
1595         updateRunningAccounts(null /* Don't sync any target */);
1596 
1597         cancelActiveSync(
1598                 new SyncStorageEngine.EndPoint(
1599                         null /* any account */,
1600                         null /* any authority */,
1601                         userId),
1602                 null /* any sync. */
1603         );
1604     }
1605 
onUserRemoved(int userId)1606     private void onUserRemoved(int userId) {
1607         updateRunningAccounts(null /* Don't sync any target */);
1608 
1609         // Clean up the storage engine database
1610         mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
1611         List<SyncOperation> ops = getAllPendingSyncs();
1612         for (SyncOperation op: ops) {
1613             if (op.target.userId == userId) {
1614                 getJobScheduler().cancel(op.jobId);
1615             }
1616         }
1617     }
1618 
1619     /**
1620      * @hide
1621      */
1622     class ActiveSyncContext extends ISyncContext.Stub
1623             implements ServiceConnection, IBinder.DeathRecipient {
1624         final SyncOperation mSyncOperation;
1625         final long mHistoryRowId;
1626         ISyncAdapter mSyncAdapter;
1627         final long mStartTime;
1628         long mTimeoutStartTime;
1629         boolean mBound;
1630         final PowerManager.WakeLock mSyncWakeLock;
1631         final int mSyncAdapterUid;
1632         SyncInfo mSyncInfo;
1633         boolean mIsLinkedToDeath = false;
1634         String mEventName;
1635 
1636         /** Total bytes transferred, counted at {@link #mLastPolledTimeElapsed} */
1637         long mBytesTransferredAtLastPoll;
1638         /**
1639          * Last point in {@link SystemClock#elapsedRealtime()} at which we checked the # of bytes
1640          * transferred to/fro by this adapter.
1641          */
1642         long mLastPolledTimeElapsed;
1643 
1644         /**
1645          * Create an ActiveSyncContext for an impending sync and grab the wakelock for that
1646          * sync adapter. Since this grabs the wakelock you need to be sure to call
1647          * close() when you are done with this ActiveSyncContext, whether the sync succeeded
1648          * or not.
1649          * @param syncOperation the SyncOperation we are about to sync
1650          * @param historyRowId the row in which to record the history info for this sync
1651          * @param syncAdapterUid the UID of the application that contains the sync adapter
1652          * for this sync. This is used to attribute the wakelock hold to that application.
1653          */
ActiveSyncContext(SyncOperation syncOperation, long historyRowId, int syncAdapterUid)1654         public ActiveSyncContext(SyncOperation syncOperation, long historyRowId,
1655                 int syncAdapterUid) {
1656             super();
1657             mSyncAdapterUid = syncAdapterUid;
1658             mSyncOperation = syncOperation;
1659             mHistoryRowId = historyRowId;
1660             mSyncAdapter = null;
1661             mStartTime = SystemClock.elapsedRealtime();
1662             mTimeoutStartTime = mStartTime;
1663             mSyncWakeLock = mSyncHandler.getSyncWakeLock(mSyncOperation);
1664             mSyncWakeLock.setWorkSource(new WorkSource(syncAdapterUid));
1665             mSyncWakeLock.acquire();
1666         }
1667 
sendHeartbeat()1668         public void sendHeartbeat() {
1669             // Heartbeats are no longer used.
1670         }
1671 
onFinished(SyncResult result)1672         public void onFinished(SyncResult result) {
1673             if (Log.isLoggable(TAG, Log.VERBOSE)) Slog.v(TAG, "onFinished: " + this);
1674             // Include "this" in the message so that the handler can ignore it if this
1675             // ActiveSyncContext is no longer the mActiveSyncContext at message handling
1676             // time.
1677             sendSyncFinishedOrCanceledMessage(this, result);
1678         }
1679 
toString(StringBuilder sb)1680         public void toString(StringBuilder sb) {
1681             sb.append("startTime ").append(mStartTime)
1682                     .append(", mTimeoutStartTime ").append(mTimeoutStartTime)
1683                     .append(", mHistoryRowId ").append(mHistoryRowId)
1684                     .append(", syncOperation ").append(mSyncOperation);
1685         }
1686 
onServiceConnected(ComponentName name, IBinder service)1687         public void onServiceConnected(ComponentName name, IBinder service) {
1688             Message msg = mSyncHandler.obtainMessage();
1689             msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED;
1690             msg.obj = new ServiceConnectionData(this, service);
1691             mSyncHandler.sendMessage(msg);
1692         }
1693 
onServiceDisconnected(ComponentName name)1694         public void onServiceDisconnected(ComponentName name) {
1695             Message msg = mSyncHandler.obtainMessage();
1696             msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED;
1697             msg.obj = new ServiceConnectionData(this, null);
1698             mSyncHandler.sendMessage(msg);
1699         }
1700 
bindToSyncAdapter(ComponentName serviceComponent, int userId)1701         boolean bindToSyncAdapter(ComponentName serviceComponent, int userId) {
1702             if (Log.isLoggable(TAG, Log.VERBOSE)) {
1703                 Log.d(TAG, "bindToSyncAdapter: " + serviceComponent + ", connection " + this);
1704             }
1705             Intent intent = new Intent();
1706             intent.setAction("android.content.SyncAdapter");
1707             intent.setComponent(serviceComponent);
1708             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
1709                     com.android.internal.R.string.sync_binding_label);
1710             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
1711                     mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
1712                     null, new UserHandle(userId)));
1713             mBound = true;
1714             final boolean bindResult = mContext.bindServiceAsUser(intent, this,
1715                     Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
1716                             | Context.BIND_ALLOW_OOM_MANAGEMENT,
1717                     new UserHandle(mSyncOperation.target.userId));
1718             if (!bindResult) {
1719                 mBound = false;
1720             } else {
1721                 try {
1722                     mEventName = mSyncOperation.wakeLockName();
1723                     mBatteryStats.noteSyncStart(mEventName, mSyncAdapterUid);
1724                 } catch (RemoteException e) {
1725                 }
1726             }
1727             return bindResult;
1728         }
1729 
1730         /**
1731          * Performs the required cleanup, which is the releasing of the wakelock and
1732          * unbinding from the sync adapter (if actually bound).
1733          */
close()1734         protected void close() {
1735             if (Log.isLoggable(TAG, Log.VERBOSE)) {
1736                 Log.d(TAG, "unBindFromSyncAdapter: connection " + this);
1737             }
1738             if (mBound) {
1739                 mBound = false;
1740                 mContext.unbindService(this);
1741                 try {
1742                     mBatteryStats.noteSyncFinish(mEventName, mSyncAdapterUid);
1743                 } catch (RemoteException e) {
1744                 }
1745             }
1746             mSyncWakeLock.release();
1747             mSyncWakeLock.setWorkSource(null);
1748         }
1749 
toString()1750         public String toString() {
1751             StringBuilder sb = new StringBuilder();
1752             toString(sb);
1753             return sb.toString();
1754         }
1755 
1756         @Override
binderDied()1757         public void binderDied() {
1758             sendSyncFinishedOrCanceledMessage(this, null);
1759         }
1760     }
1761 
dump(FileDescriptor fd, PrintWriter pw)1762     protected void dump(FileDescriptor fd, PrintWriter pw) {
1763         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
1764         dumpPendingSyncs(pw);
1765         dumpPeriodicSyncs(pw);
1766         dumpSyncState(ipw);
1767         dumpSyncHistory(ipw);
1768         dumpSyncAdapters(ipw);
1769     }
1770 
formatTime(long time)1771     static String formatTime(long time) {
1772         Time tobj = new Time();
1773         tobj.set(time);
1774         return tobj.format("%Y-%m-%d %H:%M:%S");
1775     }
1776 
dumpPendingSyncs(PrintWriter pw)1777     protected void dumpPendingSyncs(PrintWriter pw) {
1778         pw.println("Pending Syncs:");
1779         List<SyncOperation> pendingSyncs = getAllPendingSyncs();
1780         int count = 0;
1781         for (SyncOperation op: pendingSyncs) {
1782             if (!op.isPeriodic) {
1783                 pw.println(op.dump(null, false));
1784                 count++;
1785             }
1786         }
1787         pw.println("Total: " + count);
1788         pw.println();
1789     }
1790 
dumpPeriodicSyncs(PrintWriter pw)1791     protected void dumpPeriodicSyncs(PrintWriter pw) {
1792         pw.println("Periodic Syncs:");
1793         List<SyncOperation> pendingSyncs = getAllPendingSyncs();
1794         int count = 0;
1795         for (SyncOperation op: pendingSyncs) {
1796             if (op.isPeriodic) {
1797                 pw.println(op.dump(null, false));
1798                 count++;
1799             }
1800         }
1801         pw.println("Total: " + count);
1802         pw.println();
1803     }
1804 
dumpSyncState(PrintWriter pw)1805     protected void dumpSyncState(PrintWriter pw) {
1806         pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
1807         pw.print("auto sync: ");
1808         List<UserInfo> users = getAllUsers();
1809         if (users != null) {
1810             for (UserInfo user : users) {
1811                 pw.print("u" + user.id + "="
1812                         + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " ");
1813             }
1814             pw.println();
1815         }
1816         pw.print("memory low: "); pw.println(mStorageIsLow);
1817         pw.print("device idle: "); pw.println(mDeviceIsIdle);
1818         pw.print("reported active: "); pw.println(mReportedSyncActive);
1819 
1820         final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
1821 
1822         pw.print("accounts: ");
1823         if (accounts != INITIAL_ACCOUNTS_ARRAY) {
1824             pw.println(accounts.length);
1825         } else {
1826             pw.println("not known yet");
1827         }
1828         final long now = SystemClock.elapsedRealtime();
1829         pw.print("now: "); pw.print(now);
1830         pw.println(" (" + formatTime(System.currentTimeMillis()) + ")");
1831         pw.println(" (HH:MM:SS)");
1832         pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now / 1000));
1833         pw.println(" (HH:MM:SS)");
1834         pw.print("time spent syncing: ");
1835         pw.print(DateUtils.formatElapsedTime(
1836                 mSyncHandler.mSyncTimeTracker.timeSpentSyncing() / 1000));
1837         pw.print(" (HH:MM:SS), sync ");
1838         pw.print(mSyncHandler.mSyncTimeTracker.mLastWasSyncing ? "" : "not ");
1839         pw.println("in progress");
1840 
1841         pw.println();
1842         pw.println("Active Syncs: " + mActiveSyncContexts.size());
1843         final PackageManager pm = mContext.getPackageManager();
1844         for (SyncManager.ActiveSyncContext activeSyncContext : mActiveSyncContexts) {
1845             final long durationInSeconds = (now - activeSyncContext.mStartTime) / 1000;
1846             pw.print("  ");
1847             pw.print(DateUtils.formatElapsedTime(durationInSeconds));
1848             pw.print(" - ");
1849             pw.print(activeSyncContext.mSyncOperation.dump(pm, false));
1850             pw.println();
1851         }
1852 
1853         // Join the installed sync adapter with the accounts list and emit for everything.
1854         pw.println();
1855         pw.println("Sync Status");
1856         for (AccountAndUser account : accounts) {
1857             pw.printf("Account %s u%d %s\n",
1858                     account.account.name, account.userId, account.account.type);
1859 
1860             pw.println("=======================================================================");
1861             final PrintTable table = new PrintTable(13);
1862             table.set(0, 0,
1863                     "Authority", // 0
1864                     "Syncable",  // 1
1865                     "Enabled",   // 2
1866                     "Delay",     // 3
1867                     "Loc",       // 4
1868                     "Poll",      // 5
1869                     "Per",       // 6
1870                     "Serv",      // 7
1871                     "User",      // 8
1872                     "Tot",       // 9
1873                     "Time",      // 10
1874                     "Last Sync", // 11
1875                     "Etc"        // 12
1876             );
1877 
1878             final List<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> sorted =
1879                     Lists.newArrayList();
1880             sorted.addAll(mSyncAdapters.getAllServices(account.userId));
1881             Collections.sort(sorted,
1882                     new Comparator<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>() {
1883                         @Override
1884                         public int compare(RegisteredServicesCache.ServiceInfo<SyncAdapterType> lhs,
1885                                 RegisteredServicesCache.ServiceInfo<SyncAdapterType> rhs) {
1886                             return lhs.type.authority.compareTo(rhs.type.authority);
1887                         }
1888                     });
1889             for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : sorted) {
1890                 if (!syncAdapterType.type.accountType.equals(account.account.type)) {
1891                     continue;
1892                 }
1893                 int row = table.getNumRows();
1894                 Pair<AuthorityInfo, SyncStatusInfo> syncAuthoritySyncStatus =
1895                         mSyncStorageEngine.getCopyOfAuthorityWithSyncStatus(
1896                                 new SyncStorageEngine.EndPoint(
1897                                         account.account,
1898                                         syncAdapterType.type.authority,
1899                                         account.userId));
1900                 SyncStorageEngine.AuthorityInfo settings = syncAuthoritySyncStatus.first;
1901                 SyncStatusInfo status = syncAuthoritySyncStatus.second;
1902                 String authority = settings.target.provider;
1903                 if (authority.length() > 50) {
1904                     authority = authority.substring(authority.length() - 50);
1905                 }
1906                 table.set(row, 0, authority, settings.syncable, settings.enabled);
1907                 table.set(row, 4,
1908                         status.numSourceLocal,
1909                         status.numSourcePoll,
1910                         status.numSourcePeriodic,
1911                         status.numSourceServer,
1912                         status.numSourceUser,
1913                         status.numSyncs,
1914                         DateUtils.formatElapsedTime(status.totalElapsedTime / 1000));
1915 
1916                 int row1 = row;
1917                 if (settings.delayUntil > now) {
1918                     table.set(row1++, 12, "D: " + (settings.delayUntil - now) / 1000);
1919                     if (settings.backoffTime > now) {
1920                         table.set(row1++, 12, "B: " + (settings.backoffTime - now) / 1000);
1921                         table.set(row1++, 12, settings.backoffDelay / 1000);
1922                     }
1923                 }
1924 
1925                 row1 = row;
1926                 if (status.lastSuccessTime != 0) {
1927                     table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastSuccessSource]
1928                             + " " + "SUCCESS");
1929                     table.set(row1++, 11, formatTime(status.lastSuccessTime));
1930                 }
1931                 if (status.lastFailureTime != 0) {
1932                     table.set(row1++, 11, SyncStorageEngine.SOURCES[status.lastFailureSource]
1933                             + " " + "FAILURE");
1934                     table.set(row1++, 11, formatTime(status.lastFailureTime));
1935                     //noinspection UnusedAssignment
1936                     table.set(row1++, 11, status.lastFailureMesg);
1937                 }
1938             }
1939             table.writeTo(pw);
1940         }
1941     }
1942 
dumpTimeSec(PrintWriter pw, long time)1943     private void dumpTimeSec(PrintWriter pw, long time) {
1944         pw.print(time/1000); pw.print('.'); pw.print((time/100)%10);
1945         pw.print('s');
1946     }
1947 
dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds)1948     private void dumpDayStatistic(PrintWriter pw, SyncStorageEngine.DayStats ds) {
1949         pw.print("Success ("); pw.print(ds.successCount);
1950         if (ds.successCount > 0) {
1951             pw.print(" for "); dumpTimeSec(pw, ds.successTime);
1952             pw.print(" avg="); dumpTimeSec(pw, ds.successTime/ds.successCount);
1953         }
1954         pw.print(") Failure ("); pw.print(ds.failureCount);
1955         if (ds.failureCount > 0) {
1956             pw.print(" for "); dumpTimeSec(pw, ds.failureTime);
1957             pw.print(" avg="); dumpTimeSec(pw, ds.failureTime/ds.failureCount);
1958         }
1959         pw.println(")");
1960     }
1961 
dumpSyncHistory(PrintWriter pw)1962     protected void dumpSyncHistory(PrintWriter pw) {
1963         dumpRecentHistory(pw);
1964         dumpDayStatistics(pw);
1965     }
1966 
dumpRecentHistory(PrintWriter pw)1967     private void dumpRecentHistory(PrintWriter pw) {
1968         final ArrayList<SyncStorageEngine.SyncHistoryItem> items
1969                 = mSyncStorageEngine.getSyncHistory();
1970         if (items != null && items.size() > 0) {
1971             final Map<String, AuthoritySyncStats> authorityMap = Maps.newHashMap();
1972             long totalElapsedTime = 0;
1973             long totalTimes = 0;
1974             final int N = items.size();
1975 
1976             int maxAuthority = 0;
1977             int maxAccount = 0;
1978             for (SyncStorageEngine.SyncHistoryItem item : items) {
1979                 SyncStorageEngine.AuthorityInfo authorityInfo
1980                         = mSyncStorageEngine.getAuthority(item.authorityId);
1981                 final String authorityName;
1982                 final String accountKey;
1983                 if (authorityInfo != null) {
1984                     authorityName = authorityInfo.target.provider;
1985                     accountKey = authorityInfo.target.account.name + "/"
1986                             + authorityInfo.target.account.type
1987                             + " u" + authorityInfo.target.userId;
1988                 } else {
1989                     authorityName = "Unknown";
1990                     accountKey = "Unknown";
1991                 }
1992 
1993                 int length = authorityName.length();
1994                 if (length > maxAuthority) {
1995                     maxAuthority = length;
1996                 }
1997                 length = accountKey.length();
1998                 if (length > maxAccount) {
1999                     maxAccount = length;
2000                 }
2001 
2002                 final long elapsedTime = item.elapsedTime;
2003                 totalElapsedTime += elapsedTime;
2004                 totalTimes++;
2005                 AuthoritySyncStats authoritySyncStats = authorityMap.get(authorityName);
2006                 if (authoritySyncStats == null) {
2007                     authoritySyncStats = new AuthoritySyncStats(authorityName);
2008                     authorityMap.put(authorityName, authoritySyncStats);
2009                 }
2010                 authoritySyncStats.elapsedTime += elapsedTime;
2011                 authoritySyncStats.times++;
2012                 final Map<String, AccountSyncStats> accountMap = authoritySyncStats.accountMap;
2013                 AccountSyncStats accountSyncStats = accountMap.get(accountKey);
2014                 if (accountSyncStats == null) {
2015                     accountSyncStats = new AccountSyncStats(accountKey);
2016                     accountMap.put(accountKey, accountSyncStats);
2017                 }
2018                 accountSyncStats.elapsedTime += elapsedTime;
2019                 accountSyncStats.times++;
2020 
2021             }
2022 
2023             if (totalElapsedTime > 0) {
2024                 pw.println();
2025                 pw.printf("Detailed Statistics (Recent history):  "
2026                                 + "%d (# of times) %ds (sync time)\n",
2027                         totalTimes, totalElapsedTime / 1000);
2028 
2029                 final List<AuthoritySyncStats> sortedAuthorities =
2030                         new ArrayList<AuthoritySyncStats>(authorityMap.values());
2031                 Collections.sort(sortedAuthorities, new Comparator<AuthoritySyncStats>() {
2032                     @Override
2033                     public int compare(AuthoritySyncStats lhs, AuthoritySyncStats rhs) {
2034                         // reverse order
2035                         int compare = Integer.compare(rhs.times, lhs.times);
2036                         if (compare == 0) {
2037                             compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
2038                         }
2039                         return compare;
2040                     }
2041                 });
2042 
2043                 final int maxLength = Math.max(maxAuthority, maxAccount + 3);
2044                 final int padLength = 2 + 2 + maxLength + 2 + 10 + 11;
2045                 final char chars[] = new char[padLength];
2046                 Arrays.fill(chars, '-');
2047                 final String separator = new String(chars);
2048 
2049                 final String authorityFormat =
2050                         String.format("  %%-%ds: %%-9s  %%-11s\n", maxLength + 2);
2051                 final String accountFormat =
2052                         String.format("    %%-%ds:   %%-9s  %%-11s\n", maxLength);
2053 
2054                 pw.println(separator);
2055                 for (AuthoritySyncStats authoritySyncStats : sortedAuthorities) {
2056                     String name = authoritySyncStats.name;
2057                     long elapsedTime;
2058                     int times;
2059                     String timeStr;
2060                     String timesStr;
2061 
2062                     elapsedTime = authoritySyncStats.elapsedTime;
2063                     times = authoritySyncStats.times;
2064                     timeStr = String.format("%ds/%d%%",
2065                             elapsedTime / 1000,
2066                             elapsedTime * 100 / totalElapsedTime);
2067                     timesStr = String.format("%d/%d%%",
2068                             times,
2069                             times * 100 / totalTimes);
2070                     pw.printf(authorityFormat, name, timesStr, timeStr);
2071 
2072                     final List<AccountSyncStats> sortedAccounts =
2073                             new ArrayList<AccountSyncStats>(
2074                                     authoritySyncStats.accountMap.values());
2075                     Collections.sort(sortedAccounts, new Comparator<AccountSyncStats>() {
2076                         @Override
2077                         public int compare(AccountSyncStats lhs, AccountSyncStats rhs) {
2078                             // reverse order
2079                             int compare = Integer.compare(rhs.times, lhs.times);
2080                             if (compare == 0) {
2081                                 compare = Long.compare(rhs.elapsedTime, lhs.elapsedTime);
2082                             }
2083                             return compare;
2084                         }
2085                     });
2086                     for (AccountSyncStats stats: sortedAccounts) {
2087                         elapsedTime = stats.elapsedTime;
2088                         times = stats.times;
2089                         timeStr = String.format("%ds/%d%%",
2090                                 elapsedTime / 1000,
2091                                 elapsedTime * 100 / totalElapsedTime);
2092                         timesStr = String.format("%d/%d%%",
2093                                 times,
2094                                 times * 100 / totalTimes);
2095                         pw.printf(accountFormat, stats.name, timesStr, timeStr);
2096                     }
2097                     pw.println(separator);
2098                 }
2099             }
2100 
2101             pw.println();
2102             pw.println("Recent Sync History");
2103             final String format = "  %-" + maxAccount + "s  %-" + maxAuthority + "s %s\n";
2104             final Map<String, Long> lastTimeMap = Maps.newHashMap();
2105             final PackageManager pm = mContext.getPackageManager();
2106             for (int i = 0; i < N; i++) {
2107                 SyncStorageEngine.SyncHistoryItem item = items.get(i);
2108                 SyncStorageEngine.AuthorityInfo authorityInfo
2109                         = mSyncStorageEngine.getAuthority(item.authorityId);
2110                 final String authorityName;
2111                 final String accountKey;
2112                 if (authorityInfo != null) {
2113                     authorityName = authorityInfo.target.provider;
2114                     accountKey = authorityInfo.target.account.name + "/"
2115                             + authorityInfo.target.account.type
2116                             + " u" + authorityInfo.target.userId;
2117                 } else {
2118                     authorityName = "Unknown";
2119                     accountKey = "Unknown";
2120                 }
2121                 final long elapsedTime = item.elapsedTime;
2122                 final Time time = new Time();
2123                 final long eventTime = item.eventTime;
2124                 time.set(eventTime);
2125 
2126                 final String key = authorityName + "/" + accountKey;
2127                 final Long lastEventTime = lastTimeMap.get(key);
2128                 final String diffString;
2129                 if (lastEventTime == null) {
2130                     diffString = "";
2131                 } else {
2132                     final long diff = (lastEventTime - eventTime) / 1000;
2133                     if (diff < 60) {
2134                         diffString = String.valueOf(diff);
2135                     } else if (diff < 3600) {
2136                         diffString = String.format("%02d:%02d", diff / 60, diff % 60);
2137                     } else {
2138                         final long sec = diff % 3600;
2139                         diffString = String.format("%02d:%02d:%02d",
2140                                 diff / 3600, sec / 60, sec % 60);
2141                     }
2142                 }
2143                 lastTimeMap.put(key, eventTime);
2144 
2145                 pw.printf("  #%-3d: %s %8s  %5.1fs  %8s",
2146                         i + 1,
2147                         formatTime(eventTime),
2148                         SyncStorageEngine.SOURCES[item.source],
2149                         ((float) elapsedTime) / 1000,
2150                         diffString);
2151                 pw.printf(format, accountKey, authorityName,
2152                         SyncOperation.reasonToString(pm, item.reason));
2153 
2154                 if (item.event != SyncStorageEngine.EVENT_STOP
2155                         || item.upstreamActivity != 0
2156                         || item.downstreamActivity != 0) {
2157                     pw.printf("    event=%d upstreamActivity=%d downstreamActivity=%d\n",
2158                             item.event,
2159                             item.upstreamActivity,
2160                             item.downstreamActivity);
2161                 }
2162                 if (item.mesg != null
2163                         && !SyncStorageEngine.MESG_SUCCESS.equals(item.mesg)) {
2164                     pw.printf("    mesg=%s\n", item.mesg);
2165                 }
2166             }
2167             pw.println();
2168             pw.println("Recent Sync History Extras");
2169             for (int i = 0; i < N; i++) {
2170                 final SyncStorageEngine.SyncHistoryItem item = items.get(i);
2171                 final Bundle extras = item.extras;
2172                 if (extras == null || extras.size() == 0) {
2173                     continue;
2174                 }
2175                 final SyncStorageEngine.AuthorityInfo authorityInfo
2176                         = mSyncStorageEngine.getAuthority(item.authorityId);
2177                 final String authorityName;
2178                 final String accountKey;
2179                 if (authorityInfo != null) {
2180                     authorityName = authorityInfo.target.provider;
2181                     accountKey = authorityInfo.target.account.name + "/"
2182                             + authorityInfo.target.account.type
2183                             + " u" + authorityInfo.target.userId;
2184                 } else {
2185                     authorityName = "Unknown";
2186                     accountKey = "Unknown";
2187                 }
2188                 final Time time = new Time();
2189                 final long eventTime = item.eventTime;
2190                 time.set(eventTime);
2191 
2192                 pw.printf("  #%-3d: %s %8s ",
2193                         i + 1,
2194                         formatTime(eventTime),
2195                         SyncStorageEngine.SOURCES[item.source]);
2196 
2197                 pw.printf(format, accountKey, authorityName, extras);
2198             }
2199         }
2200     }
2201 
dumpDayStatistics(PrintWriter pw)2202     private void dumpDayStatistics(PrintWriter pw) {
2203         SyncStorageEngine.DayStats dses[] = mSyncStorageEngine.getDayStatistics();
2204         if (dses != null && dses[0] != null) {
2205             pw.println();
2206             pw.println("Sync Statistics");
2207             pw.print("  Today:  "); dumpDayStatistic(pw, dses[0]);
2208             int today = dses[0].day;
2209             int i;
2210             SyncStorageEngine.DayStats ds;
2211 
2212             // Print each day in the current week.
2213             for (i=1; i<=6 && i < dses.length; i++) {
2214                 ds = dses[i];
2215                 if (ds == null) break;
2216                 int delta = today-ds.day;
2217                 if (delta > 6) break;
2218 
2219                 pw.print("  Day-"); pw.print(delta); pw.print(":  ");
2220                 dumpDayStatistic(pw, ds);
2221             }
2222 
2223             // Aggregate all following days into weeks and print totals.
2224             int weekDay = today;
2225             while (i < dses.length) {
2226                 SyncStorageEngine.DayStats aggr = null;
2227                 weekDay -= 7;
2228                 while (i < dses.length) {
2229                     ds = dses[i];
2230                     if (ds == null) {
2231                         i = dses.length;
2232                         break;
2233                     }
2234                     int delta = weekDay-ds.day;
2235                     if (delta > 6) break;
2236                     i++;
2237 
2238                     if (aggr == null) {
2239                         aggr = new SyncStorageEngine.DayStats(weekDay);
2240                     }
2241                     aggr.successCount += ds.successCount;
2242                     aggr.successTime += ds.successTime;
2243                     aggr.failureCount += ds.failureCount;
2244                     aggr.failureTime += ds.failureTime;
2245                 }
2246                 if (aggr != null) {
2247                     pw.print("  Week-"); pw.print((today-weekDay)/7); pw.print(": ");
2248                     dumpDayStatistic(pw, aggr);
2249                 }
2250             }
2251         }
2252     }
2253 
dumpSyncAdapters(IndentingPrintWriter pw)2254     private void dumpSyncAdapters(IndentingPrintWriter pw) {
2255         pw.println();
2256         final List<UserInfo> users = getAllUsers();
2257         if (users != null) {
2258             for (UserInfo user : users) {
2259                 pw.println("Sync adapters for " + user + ":");
2260                 pw.increaseIndent();
2261                 for (RegisteredServicesCache.ServiceInfo<?> info :
2262                         mSyncAdapters.getAllServices(user.id)) {
2263                     pw.println(info);
2264                 }
2265                 pw.decreaseIndent();
2266                 pw.println();
2267             }
2268         }
2269     }
2270 
2271     private static class AuthoritySyncStats {
2272         String name;
2273         long elapsedTime;
2274         int times;
2275         Map<String, AccountSyncStats> accountMap = Maps.newHashMap();
2276 
AuthoritySyncStats(String name)2277         private AuthoritySyncStats(String name) {
2278             this.name = name;
2279         }
2280     }
2281 
2282     private static class AccountSyncStats {
2283         String name;
2284         long elapsedTime;
2285         int times;
2286 
AccountSyncStats(String name)2287         private AccountSyncStats(String name) {
2288             this.name = name;
2289         }
2290     }
2291 
2292     /**
2293      * A helper object to keep track of the time we have spent syncing since the last boot
2294      */
2295     private class SyncTimeTracker {
2296         /** True if a sync was in progress on the most recent call to update() */
2297         boolean mLastWasSyncing = false;
2298         /** Used to track when lastWasSyncing was last set */
2299         long mWhenSyncStarted = 0;
2300         /** The cumulative time we have spent syncing */
2301         private long mTimeSpentSyncing;
2302 
2303         /** Call to let the tracker know that the sync state may have changed */
update()2304         public synchronized void update() {
2305             final boolean isSyncInProgress = !mActiveSyncContexts.isEmpty();
2306             if (isSyncInProgress == mLastWasSyncing) return;
2307             final long now = SystemClock.elapsedRealtime();
2308             if (isSyncInProgress) {
2309                 mWhenSyncStarted = now;
2310             } else {
2311                 mTimeSpentSyncing += now - mWhenSyncStarted;
2312             }
2313             mLastWasSyncing = isSyncInProgress;
2314         }
2315 
2316         /** Get how long we have been syncing, in ms */
timeSpentSyncing()2317         public synchronized long timeSpentSyncing() {
2318             if (!mLastWasSyncing) return mTimeSpentSyncing;
2319 
2320             final long now = SystemClock.elapsedRealtime();
2321             return mTimeSpentSyncing + (now - mWhenSyncStarted);
2322         }
2323     }
2324 
2325     class ServiceConnectionData {
2326         public final ActiveSyncContext activeSyncContext;
2327         public final IBinder adapter;
2328 
ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter)2329         ServiceConnectionData(ActiveSyncContext activeSyncContext, IBinder adapter) {
2330             this.activeSyncContext = activeSyncContext;
2331             this.adapter = adapter;
2332         }
2333     }
2334 
2335     /**
2336      * Handles SyncOperation Messages that are posted to the associated
2337      * HandlerThread.
2338      */
2339     class SyncHandler extends Handler {
2340         // Messages that can be sent on mHandler.
2341         private static final int MESSAGE_SYNC_FINISHED = 1;
2342         private static final int MESSAGE_RELEASE_MESSAGES_FROM_QUEUE = 2;
2343         private static final int MESSAGE_SERVICE_CONNECTED = 4;
2344         private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
2345         private static final int MESSAGE_CANCEL = 6;
2346         static final int MESSAGE_JOBSERVICE_OBJECT = 7;
2347         static final int MESSAGE_START_SYNC = 10;
2348         static final int MESSAGE_STOP_SYNC = 11;
2349         static final int MESSAGE_SCHEDULE_SYNC = 12;
2350         static final int MESSAGE_UPDATE_PERIODIC_SYNC = 13;
2351         static final int MESSAGE_REMOVE_PERIODIC_SYNC = 14;
2352 
2353         /**
2354          * Posted periodically to monitor network process for long-running syncs.
2355          * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
2356          */
2357         private static final int MESSAGE_MONITOR_SYNC = 8;
2358         private static final int MESSAGE_ACCOUNTS_UPDATED = 9;
2359 
2360         public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
2361         private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
2362 
2363         private List<Message> mUnreadyQueue = new ArrayList<Message>();
2364 
onBootCompleted()2365         void onBootCompleted() {
2366             if (Log.isLoggable(TAG, Log.VERBOSE)) {
2367                 Slog.v(TAG, "Boot completed.");
2368             }
2369             checkIfDeviceReady();
2370         }
2371 
onDeviceProvisioned()2372         void onDeviceProvisioned() {
2373             if (Log.isLoggable(TAG, Log.DEBUG)) {
2374                 Log.d(TAG, "mProvisioned=" + mProvisioned);
2375             }
2376             checkIfDeviceReady();
2377         }
2378 
checkIfDeviceReady()2379         void checkIfDeviceReady() {
2380             if (mProvisioned && mBootCompleted && mJobServiceReady) {
2381                 synchronized(this) {
2382                     mSyncStorageEngine.restoreAllPeriodicSyncs();
2383                     // Dispatch any stashed messages.
2384                     obtainMessage(MESSAGE_RELEASE_MESSAGES_FROM_QUEUE).sendToTarget();
2385                 }
2386             }
2387         }
2388 
2389         /**
2390          * Stash any messages that come to the handler before boot is complete or before the device
2391          * is properly provisioned (i.e. out of set-up wizard).
2392          * {@link #onBootCompleted()} and {@link SyncHandler#onDeviceProvisioned} both
2393          * need to come in before we start syncing.
2394          * @param msg Message to dispatch at a later point.
2395          * @return true if a message was enqueued, false otherwise. This is to avoid losing the
2396          * message if we manage to acquire the lock but by the time we do boot has completed.
2397          */
tryEnqueueMessageUntilReadyToRun(Message msg)2398         private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
2399             synchronized (this) {
2400                 if (!mBootCompleted || !mProvisioned || !mJobServiceReady) {
2401                     // Need to copy the message bc looper will recycle it.
2402                     Message m = Message.obtain(msg);
2403                     mUnreadyQueue.add(m);
2404                     return true;
2405                 } else {
2406                     return false;
2407                 }
2408             }
2409         }
2410 
SyncHandler(Looper looper)2411         public SyncHandler(Looper looper) {
2412             super(looper);
2413         }
2414 
handleMessage(Message msg)2415         public void handleMessage(Message msg) {
2416             try {
2417                 mSyncManagerWakeLock.acquire();
2418                 // We only want to enqueue sync related messages until device is ready.
2419                 // Other messages are handled without enqueuing.
2420                 if (msg.what == MESSAGE_JOBSERVICE_OBJECT) {
2421                     Slog.i(TAG, "Got SyncJobService instance.");
2422                     mSyncJobService = (SyncJobService) msg.obj;
2423                     mJobServiceReady = true;
2424                     checkIfDeviceReady();
2425                 } else if (msg.what == SyncHandler.MESSAGE_ACCOUNTS_UPDATED) {
2426                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
2427                         Slog.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
2428                     }
2429                     EndPoint targets = (EndPoint) msg.obj;
2430                     updateRunningAccountsH(targets);
2431                 } else if (msg.what == MESSAGE_RELEASE_MESSAGES_FROM_QUEUE) {
2432                     if (mUnreadyQueue != null) {
2433                         for (Message m : mUnreadyQueue) {
2434                             handleSyncMessage(m);
2435                         }
2436                         mUnreadyQueue = null;
2437                     }
2438                 } else if (tryEnqueueMessageUntilReadyToRun(msg)) {
2439                     // No work to be done.
2440                 } else {
2441                     handleSyncMessage(msg);
2442                 }
2443             } finally {
2444                 mSyncManagerWakeLock.release();
2445             }
2446         }
2447 
handleSyncMessage(Message msg)2448         private void handleSyncMessage(Message msg) {
2449             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2450 
2451             try {
2452                 mDataConnectionIsConnected = readDataConnectionState();
2453                 switch (msg.what) {
2454                     case MESSAGE_SCHEDULE_SYNC:
2455                         ScheduleSyncMessagePayload syncPayload =
2456                                 (ScheduleSyncMessagePayload) msg.obj;
2457                         SyncOperation op = syncPayload.syncOperation;
2458                         scheduleSyncOperationH(op, syncPayload.minDelayMillis);
2459                         break;
2460 
2461                     case MESSAGE_START_SYNC:
2462                         op = (SyncOperation) msg.obj;
2463                         startSyncH(op);
2464                         break;
2465 
2466                     case MESSAGE_STOP_SYNC:
2467                         op = (SyncOperation) msg.obj;
2468                         if (isLoggable) {
2469                             Slog.v(TAG, "Stop sync received.");
2470                         }
2471                         ActiveSyncContext asc = findActiveSyncContextH(op.jobId);
2472                         if (asc != null) {
2473                             runSyncFinishedOrCanceledH(null /* no result */, asc);
2474                             boolean reschedule = msg.arg1 != 0;
2475                             boolean applyBackoff = msg.arg2 != 0;
2476                             if (isLoggable) {
2477                                 Slog.v(TAG, "Stopping sync. Reschedule: " + reschedule
2478                                         + "Backoff: " + applyBackoff);
2479                             }
2480                             if (applyBackoff) {
2481                                 increaseBackoffSetting(op.target);
2482                             }
2483                             if (reschedule) {
2484                                 deferStoppedSyncH(op, 0);
2485                             }
2486                         }
2487                         break;
2488 
2489                     case MESSAGE_UPDATE_PERIODIC_SYNC:
2490                         UpdatePeriodicSyncMessagePayload data =
2491                                 (UpdatePeriodicSyncMessagePayload) msg.obj;
2492                         updateOrAddPeriodicSyncH(data.target, data.pollFrequency,
2493                                 data.flex, data.extras);
2494                         break;
2495                     case MESSAGE_REMOVE_PERIODIC_SYNC:
2496                         removePeriodicSyncH((EndPoint)msg.obj, msg.getData());
2497                         break;
2498 
2499                     case SyncHandler.MESSAGE_CANCEL:
2500                         SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
2501                         Bundle extras = msg.peekData();
2502                         if (Log.isLoggable(TAG, Log.DEBUG)) {
2503                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_CANCEL: "
2504                                     + endpoint + " bundle: " + extras);
2505                         }
2506                         cancelActiveSyncH(endpoint, extras);
2507                         break;
2508 
2509                     case SyncHandler.MESSAGE_SYNC_FINISHED:
2510                         SyncFinishedOrCancelledMessagePayload payload =
2511                                 (SyncFinishedOrCancelledMessagePayload) msg.obj;
2512                         if (!isSyncStillActiveH(payload.activeSyncContext)) {
2513                             Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
2514                                     + "sync is no longer active: "
2515                                     + payload.activeSyncContext);
2516                             break;
2517                         }
2518                         if (isLoggable) {
2519                             Slog.v(TAG, "syncFinished" + payload.activeSyncContext.mSyncOperation);
2520                         }
2521                         mSyncJobService.callJobFinished(
2522                                 payload.activeSyncContext.mSyncOperation.jobId, false);
2523                         runSyncFinishedOrCanceledH(payload.syncResult,
2524                                 payload.activeSyncContext);
2525                         break;
2526 
2527                     case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
2528                         ServiceConnectionData msgData = (ServiceConnectionData) msg.obj;
2529                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2530                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
2531                                     + msgData.activeSyncContext);
2532                         }
2533                         // Check that this isn't an old message.
2534                         if (isSyncStillActiveH(msgData.activeSyncContext)) {
2535                             runBoundToAdapterH(
2536                                     msgData.activeSyncContext,
2537                                     msgData.adapter);
2538                         }
2539                         break;
2540                     }
2541 
2542                     case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
2543                         final ActiveSyncContext currentSyncContext =
2544                                 ((ServiceConnectionData) msg.obj).activeSyncContext;
2545                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2546                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
2547                                     + currentSyncContext);
2548                         }
2549                         // Check that this isn't an old message.
2550                         if (isSyncStillActiveH(currentSyncContext)) {
2551                             // cancel the sync if we have a syncadapter, which means one is
2552                             // outstanding
2553                             try {
2554                                 if (currentSyncContext.mSyncAdapter != null) {
2555                                     currentSyncContext.mSyncAdapter.cancelSync(currentSyncContext);
2556                                 }
2557                             } catch (RemoteException e) {
2558                                 // We don't need to retry this in this case.
2559                             }
2560 
2561                             // Pretend that the sync failed with an IOException,
2562                             // which is a soft error.
2563                             SyncResult syncResult = new SyncResult();
2564                             syncResult.stats.numIoExceptions++;
2565                             mSyncJobService.callJobFinished(
2566                                     currentSyncContext.mSyncOperation.jobId, false);
2567                             runSyncFinishedOrCanceledH(syncResult, currentSyncContext);
2568                         }
2569                         break;
2570                     }
2571 
2572                     case SyncHandler.MESSAGE_MONITOR_SYNC:
2573                         ActiveSyncContext monitoredSyncContext = (ActiveSyncContext) msg.obj;
2574                         if (Log.isLoggable(TAG, Log.DEBUG)) {
2575                             Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_MONITOR_SYNC: " +
2576                                     monitoredSyncContext.mSyncOperation.target);
2577                         }
2578 
2579                         if (isSyncNotUsingNetworkH(monitoredSyncContext)) {
2580                             Log.w(TAG, String.format(
2581                                     "Detected sync making no progress for %s. cancelling.",
2582                                     monitoredSyncContext));
2583                             mSyncJobService.callJobFinished(
2584                                     monitoredSyncContext.mSyncOperation.jobId, false);
2585                             runSyncFinishedOrCanceledH(
2586                                     null /* cancel => no result */, monitoredSyncContext);
2587                         } else {
2588                             // Repost message to check again.
2589                             postMonitorSyncProgressMessage(monitoredSyncContext);
2590                         }
2591                         break;
2592 
2593                 }
2594             } finally {
2595                 mSyncTimeTracker.update();
2596             }
2597         }
2598 
getSyncWakeLock(SyncOperation operation)2599         private PowerManager.WakeLock getSyncWakeLock(SyncOperation operation) {
2600             final String wakeLockKey = operation.wakeLockName();
2601             PowerManager.WakeLock wakeLock = mWakeLocks.get(wakeLockKey);
2602             if (wakeLock == null) {
2603                 final String name = SYNC_WAKE_LOCK_PREFIX + wakeLockKey;
2604                 wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
2605                 wakeLock.setReferenceCounted(false);
2606                 mWakeLocks.put(wakeLockKey, wakeLock);
2607             }
2608             return wakeLock;
2609         }
2610 
2611         /**
2612          * Defer the specified SyncOperation by rescheduling it on the JobScheduler with some
2613          * delay. This is equivalent to a failure. If this is a periodic sync, a delayed one-off
2614          * sync will be scheduled.
2615          */
deferSyncH(SyncOperation op, long delay)2616         private void deferSyncH(SyncOperation op, long delay) {
2617             mSyncJobService.callJobFinished(op.jobId, false);
2618             if (op.isPeriodic) {
2619                 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
2620             } else {
2621                 // mSyncJobService.callJobFinished is async, so cancel the job to ensure we don't
2622                 // find the this job in the pending jobs list while looking for duplicates
2623                 // before scheduling it at a later time.
2624                 getJobScheduler().cancel(op.jobId);
2625                 scheduleSyncOperationH(op, delay);
2626             }
2627         }
2628 
2629         /* Same as deferSyncH, but assumes that job is no longer running on JobScheduler. */
deferStoppedSyncH(SyncOperation op, long delay)2630         private void deferStoppedSyncH(SyncOperation op, long delay) {
2631             if (op.isPeriodic) {
2632                 scheduleSyncOperationH(op.createOneTimeSyncOperation(), delay);
2633             } else {
2634                 scheduleSyncOperationH(op, delay);
2635             }
2636         }
2637 
2638         /**
2639          * Cancel an active sync and reschedule it on the JobScheduler with some delay.
2640          */
deferActiveSyncH(ActiveSyncContext asc)2641         private void deferActiveSyncH(ActiveSyncContext asc) {
2642             SyncOperation op = asc.mSyncOperation;
2643             runSyncFinishedOrCanceledH(null, asc);
2644             deferSyncH(op, SYNC_DELAY_ON_CONFLICT);
2645         }
2646 
startSyncH(SyncOperation op)2647         private void startSyncH(SyncOperation op) {
2648             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2649             if (isLoggable) Slog.v(TAG, op.toString());
2650 
2651             if (mStorageIsLow) {
2652                 deferSyncH(op, SYNC_DELAY_ON_LOW_STORAGE);
2653                 return;
2654             }
2655 
2656             if (op.isPeriodic) {
2657                 // Don't allow this periodic to run if a previous instance failed and is currently
2658                 // scheduled according to some backoff criteria.
2659                 List<SyncOperation> ops = getAllPendingSyncs();
2660                 for (SyncOperation syncOperation: ops) {
2661                     if (syncOperation.sourcePeriodicId == op.jobId) {
2662                         mSyncJobService.callJobFinished(op.jobId, false);
2663                         return;
2664                     }
2665                 }
2666                 // Don't allow this periodic to run if a previous instance failed and is currently
2667                 // executing according to some backoff criteria.
2668                 for (ActiveSyncContext asc: mActiveSyncContexts) {
2669                     if (asc.mSyncOperation.sourcePeriodicId == op.jobId) {
2670                         mSyncJobService.callJobFinished(op.jobId, false);
2671                         return;
2672                     }
2673                 }
2674                 // Check for adapter delays.
2675                 if (isAdapterDelayed(op.target)) {
2676                     deferSyncH(op, 0 /* No minimum delay */);
2677                     return;
2678                 }
2679             }
2680 
2681             // Check for conflicting syncs.
2682             for (ActiveSyncContext asc: mActiveSyncContexts) {
2683                 if (asc.mSyncOperation.isConflict(op)) {
2684                     // If the provided SyncOperation conflicts with a running one, the lower
2685                     // priority sync is pre-empted.
2686                     if (asc.mSyncOperation.findPriority() >= op.findPriority()) {
2687                         if (isLoggable) {
2688                             Slog.v(TAG, "Rescheduling sync due to conflict " + op.toString());
2689                         }
2690                         deferSyncH(op, SYNC_DELAY_ON_CONFLICT);
2691                         return;
2692                     } else {
2693                         if (isLoggable) {
2694                             Slog.v(TAG, "Pushing back running sync due to a higher priority sync");
2695                         }
2696                         deferActiveSyncH(asc);
2697                         break;
2698                     }
2699                 }
2700             }
2701 
2702             final int syncOpState = computeSyncOpState(op);
2703             switch (syncOpState) {
2704                 case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS:
2705                 case SYNC_OP_STATE_INVALID: {
2706                     mSyncJobService.callJobFinished(op.jobId, false);
2707                 } return;
2708             }
2709 
2710             if (!dispatchSyncOperation(op)) {
2711                 mSyncJobService.callJobFinished(op.jobId, false);
2712             }
2713 
2714             setAuthorityPendingState(op.target);
2715         }
2716 
findActiveSyncContextH(int jobId)2717         private ActiveSyncContext findActiveSyncContextH(int jobId) {
2718             for (ActiveSyncContext asc: mActiveSyncContexts) {
2719                 SyncOperation op = asc.mSyncOperation;
2720                 if (op != null && op.jobId == jobId) {
2721                     return asc;
2722                 }
2723             }
2724             return null;
2725         }
2726 
updateRunningAccountsH(EndPoint syncTargets)2727         private void updateRunningAccountsH(EndPoint syncTargets) {
2728             AccountAndUser[] oldAccounts = mRunningAccounts;
2729             mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
2730             if (Log.isLoggable(TAG, Log.VERBOSE)) {
2731                 Slog.v(TAG, "Accounts list: ");
2732                 for (AccountAndUser acc : mRunningAccounts) {
2733                     Slog.v(TAG, acc.toString());
2734                 }
2735             }
2736             if (mBootCompleted) {
2737                 doDatabaseCleanup();
2738             }
2739 
2740             AccountAndUser[] accounts = mRunningAccounts;
2741             for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
2742                 if (!containsAccountAndUser(accounts,
2743                         currentSyncContext.mSyncOperation.target.account,
2744                         currentSyncContext.mSyncOperation.target.userId)) {
2745                     Log.d(TAG, "canceling sync since the account is no longer running");
2746                     sendSyncFinishedOrCanceledMessage(currentSyncContext,
2747                             null /* no result since this is a cancel */);
2748                 }
2749             }
2750 
2751             // On account add, check if there are any settings to be restored.
2752             for (AccountAndUser aau : mRunningAccounts) {
2753                 if (!containsAccountAndUser(oldAccounts, aau.account, aau.userId)) {
2754                     if (Log.isLoggable(TAG, Log.DEBUG)) {
2755                         Log.d(TAG, "Account " + aau.account + " added, checking sync restore data");
2756                     }
2757                     AccountSyncSettingsBackupHelper.accountAdded(mContext);
2758                     break;
2759                 }
2760             }
2761 
2762             // Cancel all jobs from non-existent accounts.
2763             AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts();
2764             List<SyncOperation> ops = getAllPendingSyncs();
2765             for (SyncOperation op: ops) {
2766                 if (!containsAccountAndUser(allAccounts, op.target.account, op.target.userId)) {
2767                     getJobScheduler().cancel(op.jobId);
2768                 }
2769             }
2770 
2771             if (syncTargets != null) {
2772                 scheduleSync(syncTargets.account, syncTargets.userId,
2773                         SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider,
2774                 null, AuthorityInfo.NOT_INITIALIZED);
2775             }
2776         }
2777 
2778         /**
2779          * The given SyncOperation will be removed and a new one scheduled in its place if
2780          * an updated period or flex is specified.
2781          * @param syncOperation SyncOperation whose period and flex is to be updated.
2782          * @param pollFrequencyMillis new period in milliseconds.
2783          * @param flexMillis new flex time in milliseconds.
2784          */
maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis, long flexMillis)2785         private void maybeUpdateSyncPeriodH(SyncOperation syncOperation, long pollFrequencyMillis,
2786                 long flexMillis) {
2787             if (!(pollFrequencyMillis == syncOperation.periodMillis
2788                     && flexMillis == syncOperation.flexMillis)) {
2789                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2790                     Slog.v(TAG, "updating period " + syncOperation + " to " + pollFrequencyMillis
2791                             + " and flex to " + flexMillis);
2792                 }
2793                 SyncOperation newOp = new SyncOperation(syncOperation, pollFrequencyMillis,
2794                         flexMillis);
2795                 newOp.jobId = syncOperation.jobId;
2796                 scheduleSyncOperationH(newOp);
2797             }
2798         }
2799 
updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex, Bundle extras)2800         private void updateOrAddPeriodicSyncH(EndPoint target, long pollFrequency, long flex,
2801                 Bundle extras) {
2802             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2803             verifyJobScheduler();  // Will fill in mScheduledSyncs cache if it is not already filled.
2804             final long pollFrequencyMillis = pollFrequency * 1000L;
2805             final long flexMillis = flex * 1000L;
2806             if (isLoggable) {
2807                 Slog.v(TAG, "Addition to periodic syncs requested: " + target
2808                         + " period: " + pollFrequency
2809                         + " flexMillis: " + flex
2810                         + " extras: " + extras.toString());
2811             }
2812             List<SyncOperation> ops = getAllPendingSyncs();
2813             for (SyncOperation op: ops) {
2814                 if (op.isPeriodic && op.target.matchesSpec(target)
2815                         && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
2816                     maybeUpdateSyncPeriodH(op, pollFrequencyMillis, flexMillis);
2817                     return;
2818                 }
2819             }
2820 
2821             if (isLoggable) {
2822                 Slog.v(TAG, "Adding new periodic sync: " + target
2823                         + " period: " + pollFrequency
2824                         + " flexMillis: " + flex
2825                         + " extras: " + extras.toString());
2826             }
2827 
2828             final RegisteredServicesCache.ServiceInfo<SyncAdapterType>
2829                     syncAdapterInfo = mSyncAdapters.getServiceInfo(
2830                     SyncAdapterType.newKey(
2831                             target.provider, target.account.type),
2832                     target.userId);
2833             if (syncAdapterInfo == null) {
2834                 return;
2835             }
2836 
2837             SyncOperation op = new SyncOperation(target, syncAdapterInfo.uid,
2838                     syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC,
2839                     SyncStorageEngine.SOURCE_PERIODIC, extras,
2840                     syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID,
2841                     pollFrequencyMillis, flexMillis);
2842 
2843             final int syncOpState = computeSyncOpState(op);
2844             switch (syncOpState) {
2845                 case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS: {
2846                     String packageName = op.owningPackage;
2847                     final int userId = UserHandle.getUserId(op.owningUid);
2848                     // If the app did not run and has no account access, done
2849                     if (!mPackageManagerInternal.wasPackageEverLaunched(packageName, userId)) {
2850                         return;
2851                     }
2852                     mAccountManagerInternal.requestAccountAccess(op.target.account,
2853                             packageName, userId, new RemoteCallback((Bundle result) -> {
2854                                 if (result != null
2855                                         && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
2856                                     updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
2857                                 }
2858                             }
2859                         ));
2860                 } return;
2861 
2862                 case SYNC_OP_STATE_INVALID: {
2863                     return;
2864                 }
2865             }
2866 
2867             scheduleSyncOperationH(op);
2868             mSyncStorageEngine.reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
2869         }
2870 
2871         /**
2872          * Remove this periodic sync operation and all one-off operations initiated by it.
2873          */
removePeriodicSyncInternalH(SyncOperation syncOperation)2874         private void removePeriodicSyncInternalH(SyncOperation syncOperation) {
2875             // Remove this periodic sync and all one-off syncs initiated by it.
2876             List<SyncOperation> ops = getAllPendingSyncs();
2877             for (SyncOperation op: ops) {
2878                 if (op.sourcePeriodicId == syncOperation.jobId || op.jobId == syncOperation.jobId) {
2879                     ActiveSyncContext asc = findActiveSyncContextH(syncOperation.jobId);
2880                     if (asc != null) {
2881                         mSyncJobService.callJobFinished(syncOperation.jobId, false);
2882                         runSyncFinishedOrCanceledH(null, asc);
2883                     }
2884                     getJobScheduler().cancel(op.jobId);
2885                 }
2886             }
2887         }
2888 
removePeriodicSyncH(EndPoint target, Bundle extras)2889         private void removePeriodicSyncH(EndPoint target, Bundle extras) {
2890             verifyJobScheduler();
2891             List<SyncOperation> ops = getAllPendingSyncs();
2892             for (SyncOperation op: ops) {
2893                 if (op.isPeriodic && op.target.matchesSpec(target)
2894                         && syncExtrasEquals(op.extras, extras, true /* includeSyncSettings */)) {
2895                     removePeriodicSyncInternalH(op);
2896                 }
2897             }
2898         }
2899 
isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext)2900         private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) {
2901             final long bytesTransferredCurrent =
2902                     getTotalBytesTransferredByUid(activeSyncContext.mSyncAdapterUid);
2903             final long deltaBytesTransferred =
2904                     bytesTransferredCurrent - activeSyncContext.mBytesTransferredAtLastPoll;
2905 
2906             if (Log.isLoggable(TAG, Log.DEBUG)) {
2907                 // Bytes transferred
2908                 long remainder = deltaBytesTransferred;
2909                 long mb = remainder / (1024 * 1024);
2910                 remainder %= 1024 * 1024;
2911                 long kb = remainder / 1024;
2912                 remainder %= 1024;
2913                 long b = remainder;
2914                 Log.d(TAG, String.format(
2915                         "Time since last update: %ds. Delta transferred: %dMBs,%dKBs,%dBs",
2916                         (SystemClock.elapsedRealtime()
2917                                 - activeSyncContext.mLastPolledTimeElapsed)/1000,
2918                         mb, kb, b)
2919                 );
2920             }
2921             return (deltaBytesTransferred <= SYNC_MONITOR_PROGRESS_THRESHOLD_BYTES);
2922         }
2923 
2924         /**
2925          * Determine if a sync is no longer valid and should be dropped.
2926          */
computeSyncOpState(SyncOperation op)2927         private int computeSyncOpState(SyncOperation op) {
2928             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
2929             int state;
2930             final EndPoint target = op.target;
2931 
2932             // Drop the sync if the account of this operation no longer exists.
2933             AccountAndUser[] accounts = mRunningAccounts;
2934             if (!containsAccountAndUser(accounts, target.account, target.userId)) {
2935                 if (isLoggable) {
2936                     Slog.v(TAG, "    Dropping sync operation: account doesn't exist.");
2937                 }
2938                 return SYNC_OP_STATE_INVALID;
2939             }
2940             // Drop this sync request if it isn't syncable.
2941             state = computeSyncable(target.account, target.userId, target.provider);
2942             if (state == AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS) {
2943                 if (isLoggable) {
2944                     Slog.v(TAG, "    Dropping sync operation: "
2945                             + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS");
2946                 }
2947                 return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS;
2948             }
2949             if (state == AuthorityInfo.NOT_SYNCABLE) {
2950                 if (isLoggable) {
2951                     Slog.v(TAG, "    Dropping sync operation: isSyncable == NOT_SYNCABLE");
2952                 }
2953                 return SYNC_OP_STATE_INVALID;
2954             }
2955 
2956             final boolean syncEnabled = mSyncStorageEngine.getMasterSyncAutomatically(target.userId)
2957                     && mSyncStorageEngine.getSyncAutomatically(target.account,
2958                             target.userId, target.provider);
2959 
2960             // We ignore system settings that specify the sync is invalid if:
2961             // 1) It's manual - we try it anyway. When/if it fails it will be rescheduled.
2962             //      or
2963             // 2) it's an initialisation sync - we just need to connect to it.
2964             final boolean ignoreSystemConfiguration = op.isIgnoreSettings() || (state < 0);
2965 
2966             // Sync not enabled.
2967             if (!syncEnabled && !ignoreSystemConfiguration) {
2968                 if (isLoggable) {
2969                     Slog.v(TAG, "    Dropping sync operation: disallowed by settings/network.");
2970                 }
2971                 return SYNC_OP_STATE_INVALID;
2972             }
2973             return SYNC_OP_STATE_VALID;
2974         }
2975 
dispatchSyncOperation(SyncOperation op)2976         private boolean dispatchSyncOperation(SyncOperation op) {
2977             if (Log.isLoggable(TAG, Log.VERBOSE)) {
2978                 Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op);
2979                 Slog.v(TAG, "num active syncs: " + mActiveSyncContexts.size());
2980                 for (ActiveSyncContext syncContext : mActiveSyncContexts) {
2981                     Slog.v(TAG, syncContext.toString());
2982                 }
2983             }
2984             // Connect to the sync adapter.
2985             int targetUid;
2986             ComponentName targetComponent;
2987             final SyncStorageEngine.EndPoint info = op.target;
2988             SyncAdapterType syncAdapterType =
2989                     SyncAdapterType.newKey(info.provider, info.account.type);
2990             final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
2991             syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, info.userId);
2992             if (syncAdapterInfo == null) {
2993                 Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
2994                         + ", removing settings for it");
2995                 mSyncStorageEngine.removeAuthority(info);
2996                 return false;
2997             }
2998             targetUid = syncAdapterInfo.uid;
2999             targetComponent = syncAdapterInfo.componentName;
3000             ActiveSyncContext activeSyncContext =
3001                     new ActiveSyncContext(op, insertStartSyncEvent(op), targetUid);
3002             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3003                 Slog.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
3004             }
3005 
3006             activeSyncContext.mSyncInfo = mSyncStorageEngine.addActiveSync(activeSyncContext);
3007             mActiveSyncContexts.add(activeSyncContext);
3008 
3009             // Post message to begin monitoring this sync's progress.
3010             postMonitorSyncProgressMessage(activeSyncContext);
3011 
3012             if (!activeSyncContext.bindToSyncAdapter(targetComponent, info.userId)) {
3013                 Slog.e(TAG, "Bind attempt failed - target: " + targetComponent);
3014                 closeActiveSyncContext(activeSyncContext);
3015                 return false;
3016             }
3017 
3018             return true;
3019         }
3020 
runBoundToAdapterH(final ActiveSyncContext activeSyncContext, IBinder syncAdapter)3021         private void runBoundToAdapterH(final ActiveSyncContext activeSyncContext,
3022                 IBinder syncAdapter) {
3023             final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
3024             try {
3025                 activeSyncContext.mIsLinkedToDeath = true;
3026                 syncAdapter.linkToDeath(activeSyncContext, 0);
3027 
3028                 activeSyncContext.mSyncAdapter = ISyncAdapter.Stub.asInterface(syncAdapter);
3029                 activeSyncContext.mSyncAdapter
3030                         .startSync(activeSyncContext, syncOperation.target.provider,
3031                                 syncOperation.target.account, syncOperation.extras);
3032             } catch (RemoteException remoteExc) {
3033                 Log.d(TAG, "maybeStartNextSync: caught a RemoteException, rescheduling", remoteExc);
3034                 closeActiveSyncContext(activeSyncContext);
3035                 increaseBackoffSetting(syncOperation.target);
3036                 scheduleSyncOperationH(syncOperation);
3037             } catch (RuntimeException exc) {
3038                 closeActiveSyncContext(activeSyncContext);
3039                 Slog.e(TAG, "Caught RuntimeException while starting the sync " + syncOperation, exc);
3040             }
3041         }
3042 
3043         /**
3044          * Cancel the sync for the provided target that matches the given bundle.
3045          * @param info Can have null fields to indicate all the active syncs for that field.
3046          * @param extras Can be null to indicate <strong>all</strong> syncs for the given endpoint.
3047          */
cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras)3048         private void cancelActiveSyncH(SyncStorageEngine.EndPoint info, Bundle extras) {
3049             ArrayList<ActiveSyncContext> activeSyncs =
3050                     new ArrayList<ActiveSyncContext>(mActiveSyncContexts);
3051             for (ActiveSyncContext activeSyncContext : activeSyncs) {
3052                 if (activeSyncContext != null) {
3053                     final SyncStorageEngine.EndPoint opInfo =
3054                             activeSyncContext.mSyncOperation.target;
3055                     if (!opInfo.matchesSpec(info)) {
3056                         continue;
3057                     }
3058                     if (extras != null &&
3059                             !syncExtrasEquals(activeSyncContext.mSyncOperation.extras,
3060                                     extras,
3061                                     false /* no config settings */)) {
3062                         continue;
3063                     }
3064                     mSyncJobService.callJobFinished(activeSyncContext.mSyncOperation.jobId, false);
3065                     runSyncFinishedOrCanceledH(null /* cancel => no result */, activeSyncContext);
3066                 }
3067             }
3068         }
3069 
3070         /**
3071          * Should be called when a one-off instance of a periodic sync completes successfully.
3072          */
reschedulePeriodicSyncH(SyncOperation syncOperation)3073         private void reschedulePeriodicSyncH(SyncOperation syncOperation) {
3074             // Ensure that the periodic sync wasn't removed.
3075             SyncOperation periodicSync = null;
3076             List<SyncOperation> ops = getAllPendingSyncs();
3077             for (SyncOperation op: ops) {
3078                 if (op.isPeriodic && syncOperation.matchesPeriodicOperation(op)) {
3079                     periodicSync = op;
3080                     break;
3081                 }
3082             }
3083             if (periodicSync == null) {
3084                 return;
3085             }
3086             scheduleSyncOperationH(periodicSync);
3087         }
3088 
runSyncFinishedOrCanceledH(SyncResult syncResult, ActiveSyncContext activeSyncContext)3089         private void runSyncFinishedOrCanceledH(SyncResult syncResult,
3090                 ActiveSyncContext activeSyncContext) {
3091             final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
3092 
3093             final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
3094             final SyncStorageEngine.EndPoint info = syncOperation.target;
3095 
3096             if (activeSyncContext.mIsLinkedToDeath) {
3097                 activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
3098                 activeSyncContext.mIsLinkedToDeath = false;
3099             }
3100             closeActiveSyncContext(activeSyncContext);
3101             final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime;
3102             String historyMessage;
3103             int downstreamActivity;
3104             int upstreamActivity;
3105 
3106             if (!syncOperation.isPeriodic) {
3107                 // mSyncJobService.jobFinidhed is async, we need to ensure that this job is
3108                 // removed from JobScheduler's pending jobs list before moving forward and
3109                 // potentially rescheduling all pending jobs to respect new backoff values.
3110                 getJobScheduler().cancel(syncOperation.jobId);
3111             }
3112 
3113             if (syncResult != null) {
3114                 if (isLoggable) {
3115                     Slog.v(TAG, "runSyncFinishedOrCanceled [finished]: "
3116                             + syncOperation + ", result " + syncResult);
3117                 }
3118 
3119                 if (!syncResult.hasError()) {
3120                     historyMessage = SyncStorageEngine.MESG_SUCCESS;
3121                     // TODO: set these correctly when the SyncResult is extended to include it
3122                     downstreamActivity = 0;
3123                     upstreamActivity = 0;
3124                     clearBackoffSetting(syncOperation.target);
3125 
3126                     // If the operation completes successfully and it was scheduled due to
3127                     // a periodic operation failing, we reschedule the periodic operation to
3128                     // start from now.
3129                     if (syncOperation.isDerivedFromFailedPeriodicSync()) {
3130                         reschedulePeriodicSyncH(syncOperation);
3131                     }
3132                 } else {
3133                     Log.d(TAG, "failed sync operation " + syncOperation + ", " + syncResult);
3134                     // the operation failed so increase the backoff time
3135                     increaseBackoffSetting(syncOperation.target);
3136                     if (!syncOperation.isPeriodic) {
3137                         // reschedule the sync if so indicated by the syncResult
3138                         maybeRescheduleSync(syncResult, syncOperation);
3139                     } else {
3140                         // create a normal sync instance that will respect adapter backoffs
3141                         postScheduleSyncMessage(syncOperation.createOneTimeSyncOperation(),
3142                                 0 /* min delay */);
3143                     }
3144                     historyMessage = ContentResolver.syncErrorToString(
3145                             syncResultToErrorNumber(syncResult));
3146                     // TODO: set these correctly when the SyncResult is extended to include it
3147                     downstreamActivity = 0;
3148                     upstreamActivity = 0;
3149                 }
3150                 setDelayUntilTime(syncOperation.target, syncResult.delayUntil);
3151             } else {
3152                 if (isLoggable) {
3153                     Slog.v(TAG, "runSyncFinishedOrCanceled [canceled]: " + syncOperation);
3154                 }
3155                 if (activeSyncContext.mSyncAdapter != null) {
3156                     try {
3157                         activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
3158                     } catch (RemoteException e) {
3159                         // we don't need to retry this in this case
3160                     }
3161                 }
3162                 historyMessage = SyncStorageEngine.MESG_CANCELED;
3163                 downstreamActivity = 0;
3164                 upstreamActivity = 0;
3165             }
3166 
3167             stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
3168                     upstreamActivity, downstreamActivity, elapsedTime);
3169             // Check for full-resync and schedule it after closing off the last sync.
3170             if (syncResult != null && syncResult.tooManyDeletions) {
3171                 installHandleTooManyDeletesNotification(info.account,
3172                         info.provider, syncResult.stats.numDeletes,
3173                         info.userId);
3174             } else {
3175                 mNotificationMgr.cancelAsUser(
3176                         Integer.toString(info.account.hashCode() ^ info.provider.hashCode()),
3177                         SystemMessage.NOTE_SYNC_ERROR,
3178                         new UserHandle(info.userId));
3179             }
3180             if (syncResult != null && syncResult.fullSyncRequested) {
3181                 scheduleSyncOperationH(
3182                         new SyncOperation(info.account, info.userId,
3183                                 syncOperation.owningUid, syncOperation.owningPackage,
3184                                 syncOperation.reason,
3185                                 syncOperation.syncSource, info.provider, new Bundle(),
3186                                 syncOperation.allowParallelSyncs));
3187             }
3188         }
3189 
closeActiveSyncContext(ActiveSyncContext activeSyncContext)3190         private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) {
3191             activeSyncContext.close();
3192             mActiveSyncContexts.remove(activeSyncContext);
3193             mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo,
3194                     activeSyncContext.mSyncOperation.target.userId);
3195 
3196             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3197                 Slog.v(TAG, "removing all MESSAGE_MONITOR_SYNC & MESSAGE_SYNC_EXPIRED for "
3198                         + activeSyncContext.toString());
3199             }
3200             mSyncHandler.removeMessages(SyncHandler.MESSAGE_MONITOR_SYNC, activeSyncContext);
3201         }
3202 
3203         /**
3204          * Convert the error-containing SyncResult into the Sync.History error number. Since
3205          * the SyncResult may indicate multiple errors at once, this method just returns the
3206          * most "serious" error.
3207          * @param syncResult the SyncResult from which to read
3208          * @return the most "serious" error set in the SyncResult
3209          * @throws IllegalStateException if the SyncResult does not indicate any errors.
3210          *   If SyncResult.error() is true then it is safe to call this.
3211          */
syncResultToErrorNumber(SyncResult syncResult)3212         private int syncResultToErrorNumber(SyncResult syncResult) {
3213             if (syncResult.syncAlreadyInProgress)
3214                 return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
3215             if (syncResult.stats.numAuthExceptions > 0)
3216                 return ContentResolver.SYNC_ERROR_AUTHENTICATION;
3217             if (syncResult.stats.numIoExceptions > 0)
3218                 return ContentResolver.SYNC_ERROR_IO;
3219             if (syncResult.stats.numParseExceptions > 0)
3220                 return ContentResolver.SYNC_ERROR_PARSE;
3221             if (syncResult.stats.numConflictDetectedExceptions > 0)
3222                 return ContentResolver.SYNC_ERROR_CONFLICT;
3223             if (syncResult.tooManyDeletions)
3224                 return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS;
3225             if (syncResult.tooManyRetries)
3226                 return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES;
3227             if (syncResult.databaseError)
3228                 return ContentResolver.SYNC_ERROR_INTERNAL;
3229             throw new IllegalStateException("we are not in an error state, " + syncResult);
3230         }
3231 
installHandleTooManyDeletesNotification(Account account, String authority, long numDeletes, int userId)3232         private void installHandleTooManyDeletesNotification(Account account, String authority,
3233                 long numDeletes, int userId) {
3234             if (mNotificationMgr == null) return;
3235 
3236             final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
3237                     authority, 0 /* flags */);
3238             if (providerInfo == null) {
3239                 return;
3240             }
3241             CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager());
3242 
3243             Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class);
3244             clickIntent.putExtra("account", account);
3245             clickIntent.putExtra("authority", authority);
3246             clickIntent.putExtra("provider", authorityName.toString());
3247             clickIntent.putExtra("numDeletes", numDeletes);
3248 
3249             if (!isActivityAvailable(clickIntent)) {
3250                 Log.w(TAG, "No activity found to handle too many deletes.");
3251                 return;
3252             }
3253 
3254             UserHandle user = new UserHandle(userId);
3255             final PendingIntent pendingIntent = PendingIntent
3256                     .getActivityAsUser(mContext, 0, clickIntent,
3257                             PendingIntent.FLAG_CANCEL_CURRENT, null, user);
3258 
3259             CharSequence tooManyDeletesDescFormat = mContext.getResources().getText(
3260                     R.string.contentServiceTooManyDeletesNotificationDesc);
3261 
3262             Context contextForUser = getContextForUser(user);
3263             Notification notification =
3264                     new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT)
3265                     .setSmallIcon(R.drawable.stat_notify_sync_error)
3266                     .setTicker(mContext.getString(R.string.contentServiceSync))
3267                     .setWhen(System.currentTimeMillis())
3268                     .setColor(contextForUser.getColor(
3269                             com.android.internal.R.color.system_notification_accent_color))
3270                     .setContentTitle(contextForUser.getString(
3271                             R.string.contentServiceSyncNotificationTitle))
3272                     .setContentText(
3273                             String.format(tooManyDeletesDescFormat.toString(), authorityName))
3274                     .setContentIntent(pendingIntent)
3275                     .build();
3276             notification.flags |= Notification.FLAG_ONGOING_EVENT;
3277             mNotificationMgr.notifyAsUser(
3278                     Integer.toString(account.hashCode() ^ authority.hashCode()),
3279                     SystemMessage.NOTE_SYNC_ERROR,
3280                     notification, user);
3281         }
3282 
3283         /**
3284          * Checks whether an activity exists on the system image for the given intent.
3285          *
3286          * @param intent The intent for an activity.
3287          * @return Whether or not an activity exists.
3288          */
isActivityAvailable(Intent intent)3289         private boolean isActivityAvailable(Intent intent) {
3290             PackageManager pm = mContext.getPackageManager();
3291             List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
3292             int listSize = list.size();
3293             for (int i = 0; i < listSize; i++) {
3294                 ResolveInfo resolveInfo = list.get(i);
3295                 if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
3296                         != 0) {
3297                     return true;
3298                 }
3299             }
3300 
3301             return false;
3302         }
3303 
insertStartSyncEvent(SyncOperation syncOperation)3304         public long insertStartSyncEvent(SyncOperation syncOperation) {
3305             final long now = System.currentTimeMillis();
3306             EventLog.writeEvent(2720,
3307                     syncOperation.toEventLog(SyncStorageEngine.EVENT_START));
3308             return mSyncStorageEngine.insertStartSyncEvent(syncOperation, now);
3309         }
3310 
stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage, int upstreamActivity, int downstreamActivity, long elapsedTime)3311         public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
3312                 int upstreamActivity, int downstreamActivity, long elapsedTime) {
3313             EventLog.writeEvent(2720,
3314                     syncOperation.toEventLog(SyncStorageEngine.EVENT_STOP));
3315             mSyncStorageEngine.stopSyncEvent(rowId, elapsedTime,
3316                     resultMessage, downstreamActivity, upstreamActivity);
3317         }
3318     }
3319 
isSyncStillActiveH(ActiveSyncContext activeSyncContext)3320     private boolean isSyncStillActiveH(ActiveSyncContext activeSyncContext) {
3321         for (ActiveSyncContext sync : mActiveSyncContexts) {
3322             if (sync == activeSyncContext) {
3323                 return true;
3324             }
3325         }
3326         return false;
3327     }
3328 
3329     /**
3330      * Sync extra comparison function.
3331      * @param b1 bundle to compare
3332      * @param b2 other bundle to compare
3333      * @param includeSyncSettings if false, ignore system settings in bundle.
3334      */
syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings)3335     public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSyncSettings) {
3336         if (b1 == b2) {
3337             return true;
3338         }
3339         // Exit early if we can.
3340         if (includeSyncSettings && b1.size() != b2.size()) {
3341             return false;
3342         }
3343         Bundle bigger = b1.size() > b2.size() ? b1 : b2;
3344         Bundle smaller = b1.size() > b2.size() ? b2 : b1;
3345         for (String key : bigger.keySet()) {
3346             if (!includeSyncSettings && isSyncSetting(key)) {
3347                 continue;
3348             }
3349             if (!smaller.containsKey(key)) {
3350                 return false;
3351             }
3352             if (!Objects.equals(bigger.get(key), smaller.get(key))) {
3353                 return false;
3354             }
3355         }
3356         return true;
3357     }
3358 
3359     /**
3360      * @return true if the provided key is used by the SyncManager in scheduling the sync.
3361      */
isSyncSetting(String key)3362     private static boolean isSyncSetting(String key) {
3363         if (key.equals(ContentResolver.SYNC_EXTRAS_EXPEDITED)) {
3364             return true;
3365         }
3366         if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS)) {
3367             return true;
3368         }
3369         if (key.equals(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF)) {
3370             return true;
3371         }
3372         if (key.equals(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY)) {
3373             return true;
3374         }
3375         if (key.equals(ContentResolver.SYNC_EXTRAS_MANUAL)) {
3376             return true;
3377         }
3378         if (key.equals(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
3379             return true;
3380         }
3381         if (key.equals(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS)) {
3382             return true;
3383         }
3384         if (key.equals(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS)) {
3385             return true;
3386         }
3387         if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD)) {
3388             return true;
3389         }
3390         if (key.equals(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD)) {
3391             return true;
3392         }
3393         if (key.equals(ContentResolver.SYNC_EXTRAS_PRIORITY)) {
3394             return true;
3395         }
3396         if (key.equals(ContentResolver.SYNC_EXTRAS_DISALLOW_METERED)) {
3397             return true;
3398         }
3399         if (key.equals(ContentResolver.SYNC_EXTRAS_INITIALIZE)) {
3400             return true;
3401         }
3402         return false;
3403     }
3404 
3405     static class PrintTable {
3406         private ArrayList<Object[]> mTable = Lists.newArrayList();
3407         private final int mCols;
3408 
PrintTable(int cols)3409         PrintTable(int cols) {
3410             mCols = cols;
3411         }
3412 
set(int row, int col, Object... values)3413         void set(int row, int col, Object... values) {
3414             if (col + values.length > mCols) {
3415                 throw new IndexOutOfBoundsException("Table only has " + mCols +
3416                         " columns. can't set " + values.length + " at column " + col);
3417             }
3418             for (int i = mTable.size(); i <= row; i++) {
3419                 final Object[] list = new Object[mCols];
3420                 mTable.add(list);
3421                 for (int j = 0; j < mCols; j++) {
3422                     list[j] = "";
3423                 }
3424             }
3425             System.arraycopy(values, 0, mTable.get(row), col, values.length);
3426         }
3427 
writeTo(PrintWriter out)3428         void writeTo(PrintWriter out) {
3429             final String[] formats = new String[mCols];
3430             int totalLength = 0;
3431             for (int col = 0; col < mCols; ++col) {
3432                 int maxLength = 0;
3433                 for (Object[] row : mTable) {
3434                     final int length = row[col].toString().length();
3435                     if (length > maxLength) {
3436                         maxLength = length;
3437                     }
3438                 }
3439                 totalLength += maxLength;
3440                 formats[col] = String.format("%%-%ds", maxLength);
3441             }
3442             formats[mCols - 1] = "%s";
3443             printRow(out, formats, mTable.get(0));
3444             totalLength += (mCols - 1) * 2;
3445             for (int i = 0; i < totalLength; ++i) {
3446                 out.print("-");
3447             }
3448             out.println();
3449             for (int i = 1, mTableSize = mTable.size(); i < mTableSize; i++) {
3450                 Object[] row = mTable.get(i);
3451                 printRow(out, formats, row);
3452             }
3453         }
3454 
printRow(PrintWriter out, String[] formats, Object[] row)3455         private void printRow(PrintWriter out, String[] formats, Object[] row) {
3456             for (int j = 0, rowLength = row.length; j < rowLength; j++) {
3457                 out.printf(String.format(formats[j], row[j].toString()));
3458                 out.print("  ");
3459             }
3460             out.println();
3461         }
3462 
getNumRows()3463         public int getNumRows() {
3464             return mTable.size();
3465         }
3466     }
3467 
getContextForUser(UserHandle user)3468     private Context getContextForUser(UserHandle user) {
3469         try {
3470             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
3471         } catch (NameNotFoundException e) {
3472             // Default to mContext, not finding the package system is running as is unlikely.
3473             return mContext;
3474         }
3475     }
3476 }
3477