| 1 | // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | package org.chromium.chrome.browser.sync; |
| 6 | |
| 7 | import android.accounts.Account; |
| 8 | import android.content.ContentResolver; |
| 9 | import android.content.Context; |
| 10 | import android.content.SharedPreferences; |
| 11 | import android.os.AsyncTask; |
| 12 | import android.os.Bundle; |
| 13 | import android.preference.PreferenceManager; |
| 14 | import android.util.Log; |
| 15 | |
| 16 | import com.google.common.annotations.VisibleForTesting; |
| 17 | |
| 18 | import org.chromium.base.ActivityStatus; |
| 19 | import org.chromium.sync.notifier.SyncStatusHelper; |
| 20 | import org.chromium.sync.signin.AccountManagerHelper; |
| 21 | |
| 22 | /** |
| 23 | * A class for controlling when a sync should be performed immediately, and when it should be |
| 24 | * delayed until Chrome comes to the foreground again. |
| 25 | */ |
| 26 | public class DelayedSyncController { |
| 27 | private static final String TAG = "DelayedSyncController"; |
| 28 | private static final String DELAYED_ACCOUNT_NAME = "delayed_account"; |
| 29 | |
| 30 | private static class LazyHolder { |
| 31 | private static final DelayedSyncController INSTANCE = new DelayedSyncController(); |
| 32 | } |
| 33 | |
| 34 | public static DelayedSyncController getInstance() { |
| 35 | return LazyHolder.INSTANCE; |
| 36 | } |
| 37 | |
| 38 | @VisibleForTesting |
| 39 | DelayedSyncController() {} |
| 40 | |
| 41 | /** |
| 42 | * Resume any syncs that were delayed while Chromium was backgrounded. |
| 43 | */ |
| 44 | public boolean resumeDelayedSyncs(final Context context) { |
| 45 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); |
| 46 | String accountName = prefs.getString(DELAYED_ACCOUNT_NAME, null); |
| 47 | if (accountName == null) { |
| 48 | Log.d(TAG, "No delayed sync."); |
| 49 | return false; |
| 50 | } else { |
| 51 | Log.d(TAG, "Handling delayed sync."); |
| 52 | Account account = AccountManagerHelper.createAccountFromName(accountName); |
| 53 | requestSyncOnBackgroundThread(context, account); |
| 54 | return true; |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | /** |
| 59 | * Calls ContentResolver.requestSync() in a separate thread as it performs some blocking |
| 60 | * IO operations. |
| 61 | */ |
| 62 | @VisibleForTesting |
| 63 | void requestSyncOnBackgroundThread(final Context context, final Account account) { |
| 64 | new AsyncTask<Void, Void, Void>() { |
| 65 | @Override |
| 66 | protected Void doInBackground(Void... unused) { |
| 67 | String contractAuthority = |
| 68 | SyncStatusHelper.get(context).getContractAuthority(); |
| 69 | ContentResolver.requestSync(account, contractAuthority, new Bundle()); |
| 70 | return null; |
| 71 | } |
| 72 | }.execute(); |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Stores preferences to indicate that an invalidation has arrived, but dropped on the floor. |
| 77 | */ |
| 78 | void setDelayedSync(Context ctx, String accountName) { |
| 79 | SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(ctx).edit(); |
| 80 | editor.putString(DELAYED_ACCOUNT_NAME, accountName); |
| 81 | editor.apply(); |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * If there is a delayed sync, it will be cleared. |
| 86 | */ |
| 87 | @VisibleForTesting |
| 88 | void clearDelayedSyncs(Context context) { |
| 89 | setDelayedSync(context, null); |
| 90 | } |
| 91 | |
| 92 | @VisibleForTesting |
| 93 | boolean shouldPerformSync(Context ctx, Bundle extras, Account account) { |
| 94 | boolean manualSync = isManualSync(extras); |
| 95 | |
| 96 | if (manualSync || ActivityStatus.getState() == ActivityStatus.RESUMED) { |
| 97 | clearDelayedSyncs(ctx); |
| 98 | return true; |
| 99 | } else { |
| 100 | Log.d(TAG, "Delaying sync."); |
| 101 | setDelayedSync(ctx, account.name); |
| 102 | return false; |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | private static boolean isManualSync(Bundle extras) { |
| 107 | boolean manualSync = false; |
| 108 | if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)) { |
| 109 | manualSync = true; |
| 110 | Log.d(TAG, "Manual sync requested."); |
| 111 | } |
| 112 | return manualSync; |
| 113 | } |
| 114 | } |