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