[Sync] Make it impossible to get a reference to AndroidSyncSettings.
[chromium-blink-merge.git] / sync / android / java / src / org / chromium / sync / AndroidSyncSettings.java
blobc36cef484eef50328b7077832ac8f6051137f981
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.
5 package org.chromium.sync;
7 import android.accounts.Account;
8 import android.content.ContentResolver;
9 import android.content.Context;
10 import android.content.SyncStatusObserver;
11 import android.os.StrictMode;
13 import org.chromium.base.ObserverList;
14 import org.chromium.base.VisibleForTesting;
15 import org.chromium.sync.signin.AccountManagerHelper;
17 import javax.annotation.concurrent.ThreadSafe;
19 /**
20 * A helper class to handle the current status of sync for Chrome in Android settings.
22 * It also provides an observer to be used whenever Android sync settings change.
24 * This class is a collection of static methods so that no references to its object can be
25 * stored. This is important because tests need to be able to overwrite the object with a
26 * mock content resolver and know that no references to the old one are cached.
28 * This class must be initialized via updateAccount() on startup if the user is signed in.
30 @ThreadSafe
31 public class AndroidSyncSettings {
33 public static final String TAG = "AndroidSyncSettings";
35 /**
36 * Lock for ensuring singleton instantiation across threads.
38 private static final Object CLASS_LOCK = new Object();
40 private static AndroidSyncSettings sInstance;
42 private final Object mLock = new Object();
44 private final String mContractAuthority;
46 private final Context mApplicationContext;
48 private final SyncContentResolverDelegate mSyncContentResolverDelegate;
50 private Account mAccount = null;
52 private boolean mIsSyncable = false;
54 private boolean mChromeSyncEnabled = false;
56 private boolean mMasterSyncEnabled = false;
58 private final ObserverList<AndroidSyncSettingsObserver> mObservers =
59 new ObserverList<AndroidSyncSettingsObserver>();
61 /**
62 * Provides notifications when Android sync settings have changed.
64 public interface AndroidSyncSettingsObserver {
65 public void androidSyncSettingsChanged();
68 private static void ensureInitialized(Context context) {
69 synchronized (CLASS_LOCK) {
70 if (sInstance == null) {
71 SyncContentResolverDelegate contentResolver =
72 new SystemSyncContentResolverDelegate();
73 sInstance = new AndroidSyncSettings(context, contentResolver);
78 @VisibleForTesting
79 public static void overrideForTests(Context context,
80 SyncContentResolverDelegate contentResolver) {
81 synchronized (CLASS_LOCK) {
82 sInstance = new AndroidSyncSettings(context, contentResolver);
86 /**
87 * @param context the context the ApplicationContext will be retrieved from.
88 * @param syncContentResolverDelegate an implementation of {@link SyncContentResolverDelegate}.
90 private AndroidSyncSettings(Context context,
91 SyncContentResolverDelegate syncContentResolverDelegate) {
92 mApplicationContext = context.getApplicationContext();
93 mSyncContentResolverDelegate = syncContentResolverDelegate;
94 mContractAuthority = getContractAuthority();
96 updateCachedSettings();
98 mSyncContentResolverDelegate.addStatusChangeListener(
99 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS,
100 new AndroidSyncSettingsChangedObserver());
104 * Checks whether sync is currently enabled from Chrome for the currently signed in account.
106 * It checks both the master sync for the device, and Chrome sync setting for the given account.
107 * If no user is currently signed in it returns false.
109 * @return true if sync is on, false otherwise
111 public static boolean isSyncEnabled(Context context) {
112 ensureInitialized(context);
113 return sInstance.mMasterSyncEnabled && sInstance.mChromeSyncEnabled;
117 * Checks whether sync is currently enabled from Chrome for a given account.
119 * It checks only Chrome sync setting for the given account,
120 * and ignores the master sync setting.
122 * @return true if sync is on, false otherwise
124 public static boolean isChromeSyncEnabled(Context context) {
125 ensureInitialized(context);
126 return sInstance.mChromeSyncEnabled;
130 * Checks whether the master sync flag for Android is currently enabled.
132 public static boolean isMasterSyncEnabled(Context context) {
133 ensureInitialized(context);
134 return sInstance.mMasterSyncEnabled;
138 * Make sure Chrome is syncable, and enable sync.
140 public static void enableChromeSync(Context context) {
141 ensureInitialized(context);
142 sInstance.setChromeSyncEnabled(true);
146 * Disables Android Chrome sync
148 public static void disableChromeSync(Context context) {
149 ensureInitialized(context);
150 sInstance.setChromeSyncEnabled(false);
154 * Must be called when a new account is signed in.
156 public static void updateAccount(Context context, Account account) {
157 ensureInitialized(context);
158 synchronized (sInstance.mLock) {
159 sInstance.mAccount = account;
160 sInstance.updateSyncability();
162 if (sInstance.updateCachedSettings()) {
163 sInstance.notifyObservers();
168 * Returns the contract authority to use when requesting sync.
170 public static String getContractAuthority(Context context) {
171 ensureInitialized(context);
172 return sInstance.getContractAuthority();
176 * Add a new AndroidSyncSettingsObserver.
178 public static void registerObserver(Context context, AndroidSyncSettingsObserver observer) {
179 ensureInitialized(context);
180 synchronized (sInstance.mLock) {
181 sInstance.mObservers.addObserver(observer);
186 * Remove an AndroidSyncSettingsObserver that was previously added.
188 public static void unregisterObserver(Context context, AndroidSyncSettingsObserver observer) {
189 ensureInitialized(context);
190 synchronized (sInstance.mLock) {
191 sInstance.mObservers.removeObserver(observer);
195 private void setChromeSyncEnabled(boolean value) {
196 synchronized (mLock) {
197 updateSyncability();
198 if (value == mChromeSyncEnabled || mAccount == null) return;
199 mChromeSyncEnabled = value;
201 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
202 mSyncContentResolverDelegate.setSyncAutomatically(mAccount, mContractAuthority, value);
203 StrictMode.setThreadPolicy(oldPolicy);
205 notifyObservers();
209 * Ensure Chrome is registered with the Android Sync Manager iff signed in.
211 * This is what causes the "Chrome" option to appear in Settings -> Accounts -> Sync .
212 * This function must be called within a synchronized block.
214 private void updateSyncability() {
215 boolean shouldBeSyncable = mAccount != null;
216 if (mIsSyncable == shouldBeSyncable) return;
218 mIsSyncable = shouldBeSyncable;
220 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
221 // Make account syncable if there is one.
222 if (shouldBeSyncable) {
223 mSyncContentResolverDelegate.setIsSyncable(mAccount, mContractAuthority, 1);
226 // Disable the syncability of Chrome for all other accounts. Don't use
227 // our cache as we're touching many accounts that aren't signed in, so this saves
228 // extra calls to Android sync configuration.
229 Account[] googleAccounts = AccountManagerHelper.get(mApplicationContext)
230 .getGoogleAccounts();
231 for (Account accountToSetNotSyncable : googleAccounts) {
232 if (!accountToSetNotSyncable.equals(mAccount)
233 && mSyncContentResolverDelegate.getIsSyncable(
234 accountToSetNotSyncable, mContractAuthority) > 0) {
235 mSyncContentResolverDelegate.setIsSyncable(accountToSetNotSyncable,
236 mContractAuthority, 0);
239 StrictMode.setThreadPolicy(oldPolicy);
243 * Helper class to be used by observers whenever sync settings change.
245 * To register the observer, call AndroidSyncSettings.registerObserver(...).
247 private class AndroidSyncSettingsChangedObserver implements SyncStatusObserver {
248 @Override
249 public void onStatusChanged(int which) {
250 if (which == ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS) {
251 // Sync settings have changed; update our cached values.
252 if (updateCachedSettings()) {
253 // If something actually changed, tell our observers.
254 notifyObservers();
261 * Update the three cached settings from the content resolver.
263 * @return Whether either chromeSyncEnabled or masterSyncEnabled changed.
265 private boolean updateCachedSettings() {
266 synchronized (mLock) {
267 boolean oldChromeSyncEnabled = mChromeSyncEnabled;
268 boolean oldMasterSyncEnabled = mMasterSyncEnabled;
270 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
271 if (mAccount != null) {
272 mIsSyncable = mSyncContentResolverDelegate.getIsSyncable(
273 mAccount, mContractAuthority) == 1;
274 mChromeSyncEnabled = mSyncContentResolverDelegate.getSyncAutomatically(
275 mAccount, mContractAuthority);
276 } else {
277 mIsSyncable = false;
278 mChromeSyncEnabled = false;
280 mMasterSyncEnabled = mSyncContentResolverDelegate.getMasterSyncAutomatically();
281 StrictMode.setThreadPolicy(oldPolicy);
283 return oldChromeSyncEnabled != mChromeSyncEnabled
284 || oldMasterSyncEnabled != mMasterSyncEnabled;
288 private void notifyObservers() {
289 for (AndroidSyncSettingsObserver observer : mObservers) {
290 observer.androidSyncSettingsChanged();
294 // TODO(maxbogue): make private once downstream uses are removed.
295 @Deprecated
296 public String getContractAuthority() {
297 return mApplicationContext.getPackageName();
300 // Deprecated section; to be removed once downstream no longer uses them.
302 @Deprecated
303 public static AndroidSyncSettings get(Context context) {
304 ensureInitialized(context);
305 return sInstance;
308 @Deprecated
309 public boolean isSyncEnabled() {
310 return mMasterSyncEnabled && mChromeSyncEnabled;
313 @Deprecated
314 public boolean isChromeSyncEnabled() {
315 return mChromeSyncEnabled;
318 @Deprecated
319 public boolean isMasterSyncEnabled() {
320 return mMasterSyncEnabled;
323 @Deprecated
324 public void enableChromeSync() {
325 setChromeSyncEnabled(true);
328 @Deprecated
329 public void disableChromeSync() {
330 setChromeSyncEnabled(false);
333 @Deprecated
334 public void updateAccount(Account account) {
335 synchronized (mLock) {
336 mAccount = account;
337 updateSyncability();
339 if (updateCachedSettings()) {
340 notifyObservers();
344 @Deprecated
345 public void registerObserver(AndroidSyncSettingsObserver observer) {
346 synchronized (mLock) {
347 mObservers.addObserver(observer);
351 @Deprecated
352 public void unregisterObserver(AndroidSyncSettingsObserver observer) {
353 synchronized (mLock) {
354 mObservers.removeObserver(observer);