OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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.invalidation; |
| 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 |
| 15 import org.chromium.base.ApplicationStatus; |
| 16 import org.chromium.base.Log; |
| 17 import org.chromium.base.VisibleForTesting; |
| 18 import org.chromium.components.invalidation.PendingInvalidation; |
| 19 import org.chromium.sync.AndroidSyncSettings; |
| 20 import org.chromium.sync.signin.AccountManagerHelper; |
| 21 |
| 22 import java.util.ArrayList; |
| 23 import java.util.Arrays; |
| 24 import java.util.HashSet; |
| 25 import java.util.List; |
| 26 import java.util.Set; |
| 27 |
| 28 /** |
| 29 * A class for controlling whether an invalidation should be notified immediatel
y, or should be |
| 30 * delayed until Chrome comes to the foreground again. |
| 31 */ |
| 32 public class DelayedInvalidationsController { |
| 33 private static final String TAG = Log.makeTag("invalidation"); |
| 34 private static final String DELAYED_ACCOUNT_NAME = "delayed_account"; |
| 35 private static final String DELAYED_INVALIDATIONS = "delayed_invalidations"; |
| 36 |
| 37 private static class LazyHolder { |
| 38 private static final DelayedInvalidationsController INSTANCE = |
| 39 new DelayedInvalidationsController(); |
| 40 } |
| 41 |
| 42 public static DelayedInvalidationsController getInstance() { |
| 43 return LazyHolder.INSTANCE; |
| 44 } |
| 45 |
| 46 @VisibleForTesting |
| 47 DelayedInvalidationsController() {} |
| 48 |
| 49 /** |
| 50 * Notify any invalidations that were delayed while Chromium was backgrounde
d. |
| 51 * @return whether there were any invalidations pending to be notified. |
| 52 */ |
| 53 public boolean notifyPendingInvalidations(final Context context) { |
| 54 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
context); |
| 55 String accountName = prefs.getString(DELAYED_ACCOUNT_NAME, null); |
| 56 if (accountName == null) { |
| 57 Log.d(TAG, "No pending invalidations."); |
| 58 return false; |
| 59 } else { |
| 60 Log.d(TAG, "Handling pending invalidations."); |
| 61 Account account = AccountManagerHelper.createAccountFromName(account
Name); |
| 62 List<Bundle> bundles = popPendingInvalidations(context); |
| 63 notifyInvalidationsOnBackgroundThread(context, account, bundles); |
| 64 return true; |
| 65 } |
| 66 } |
| 67 |
| 68 /** |
| 69 * Calls ContentResolver.requestSync() in a separate thread as it performs s
ome blocking |
| 70 * IO operations. |
| 71 */ |
| 72 @VisibleForTesting |
| 73 void notifyInvalidationsOnBackgroundThread( |
| 74 final Context context, final Account account, final List<Bundle> bun
dles) { |
| 75 new AsyncTask<Void, Void, Void>() { |
| 76 @Override |
| 77 protected Void doInBackground(Void... unused) { |
| 78 String contractAuthority = AndroidSyncSettings.getContractAuthor
ity(context); |
| 79 for (Bundle bundle : bundles) { |
| 80 ContentResolver.requestSync(account, contractAuthority, bund
le); |
| 81 } |
| 82 return null; |
| 83 } |
| 84 }.execute(); |
| 85 } |
| 86 |
| 87 /** |
| 88 * Stores preferences to indicate that an invalidation has arrived, but drop
ped on the floor. |
| 89 */ |
| 90 @VisibleForTesting |
| 91 void addPendingInvalidation(Context context, String account, PendingInvalida
tion invalidation) { |
| 92 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
context); |
| 93 String oldAccount = prefs.getString(DELAYED_ACCOUNT_NAME, null); |
| 94 Set<String> invals = prefs.getStringSet(DELAYED_INVALIDATIONS, new HashS
et<String>(1)); |
| 95 assert invals.isEmpty() || oldAccount != null; |
| 96 if (oldAccount != null && !oldAccount.equals(account)) { |
| 97 invals.clear(); |
| 98 } |
| 99 SharedPreferences.Editor editor = prefs.edit(); |
| 100 editor.putString(DELAYED_ACCOUNT_NAME, account); |
| 101 if (invalidation.mObjectSource == 0 || (oldAccount != null && invals.isE
mpty())) { |
| 102 editor.putStringSet(DELAYED_INVALIDATIONS, null); |
| 103 } else { |
| 104 invals.add(invalidation.encodeToString()); |
| 105 editor.putStringSet(DELAYED_INVALIDATIONS, invals); |
| 106 } |
| 107 editor.apply(); |
| 108 } |
| 109 |
| 110 private List<Bundle> popPendingInvalidations(final Context context) { |
| 111 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
context); |
| 112 assert prefs.contains(DELAYED_ACCOUNT_NAME); |
| 113 Set<String> savedInvalidations = prefs.getStringSet(DELAYED_INVALIDATION
S, null); |
| 114 clearPendingInvalidations(context); |
| 115 // Absence of specific invalidations indicates invalidate all types. |
| 116 if (savedInvalidations == null) return Arrays.asList(new Bundle()); |
| 117 |
| 118 List<Bundle> bundles = new ArrayList<Bundle>(savedInvalidations.size()); |
| 119 for (String invalidation : savedInvalidations) { |
| 120 Bundle bundle = PendingInvalidation.decodeToBundle(invalidation); |
| 121 if (bundle == null) { |
| 122 Log.e(TAG, "Error parsing saved invalidation. Invalidating all."
); |
| 123 return Arrays.asList(new Bundle()); |
| 124 } |
| 125 bundles.add(bundle); |
| 126 } |
| 127 return bundles; |
| 128 } |
| 129 |
| 130 /** |
| 131 * If there are any pending invalidations, they will be cleared. |
| 132 */ |
| 133 @VisibleForTesting |
| 134 public void clearPendingInvalidations(Context context) { |
| 135 SharedPreferences.Editor editor = |
| 136 PreferenceManager.getDefaultSharedPreferences(context).edit(); |
| 137 editor.putString(DELAYED_ACCOUNT_NAME, null); |
| 138 editor.putStringSet(DELAYED_INVALIDATIONS, null); |
| 139 editor.apply(); |
| 140 } |
| 141 |
| 142 @VisibleForTesting |
| 143 boolean shouldNotifyInvalidation(Bundle extras) { |
| 144 return isManualRequest(extras) || ApplicationStatus.hasVisibleActivities
(); |
| 145 } |
| 146 |
| 147 private static boolean isManualRequest(Bundle extras) { |
| 148 if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)) { |
| 149 Log.d(TAG, "Manual sync requested."); |
| 150 return true; |
| 151 } |
| 152 return false; |
| 153 } |
| 154 } |
OLD | NEW |