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