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 5package org.chromium.chrome.browser.sync; 6 7import android.accounts.Account; 8import android.content.ContentResolver; 9import android.content.Context; 10import android.content.SharedPreferences; 11import android.os.AsyncTask; 12import android.os.Bundle; 13import android.preference.PreferenceManager; 14import android.util.Log; 15 16import com.google.common.annotations.VisibleForTesting; 17 18import org.chromium.base.ActivityStatus; 19import org.chromium.sync.notifier.SyncStatusHelper; 20import 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 */ 26public 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.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 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