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 getInstance()34 public static DelayedSyncController getInstance() { 35 return LazyHolder.INSTANCE; 36 } 37 38 @VisibleForTesting DelayedSyncController()39 DelayedSyncController() {} 40 41 /** 42 * Resume any syncs that were delayed while Chromium was backgrounded. 43 */ resumeDelayedSyncs(final Context context)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 requestSyncOnBackgroundThread(final Context context, final Account account)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 */ setDelayedSync(Context ctx, String accountName)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 clearDelayedSyncs(Context context)88 void clearDelayedSyncs(Context context) { 89 setDelayedSync(context, null); 90 } 91 92 @VisibleForTesting shouldPerformSync(Context ctx, Bundle extras, Account account)93 boolean shouldPerformSync(Context ctx, Bundle extras, Account account) { 94 boolean manualSync = isManualSync(extras); 95 96 if (manualSync || ActivityStatus.isApplicationVisible()) { 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 isManualSync(Bundle extras)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 } 115