Chromium Code Reviews| Index: sync/android/java/src/org/chromium/sync/AndroidSyncSettings.java |
| diff --git a/sync/android/java/src/org/chromium/sync/AndroidSyncSettings.java b/sync/android/java/src/org/chromium/sync/AndroidSyncSettings.java |
| index 99d66dafce0a1de90c3110e85d7b68fc802c9e8a..30bb35a4cfcc89a6bb33c2f6b650a9223cc25d87 100644 |
| --- a/sync/android/java/src/org/chromium/sync/AndroidSyncSettings.java |
| +++ b/sync/android/java/src/org/chromium/sync/AndroidSyncSettings.java |
| @@ -13,9 +13,7 @@ import android.os.StrictMode; |
| import org.chromium.base.ObserverList; |
| import org.chromium.base.VisibleForTesting; |
| import org.chromium.sync.signin.AccountManagerHelper; |
| -import org.chromium.sync.signin.ChromeSigninController; |
| -import javax.annotation.concurrent.NotThreadSafe; |
| import javax.annotation.concurrent.ThreadSafe; |
| /** |
| @@ -24,119 +22,14 @@ import javax.annotation.concurrent.ThreadSafe; |
| * It also provides an observer to be used whenever Android sync settings change. |
| * |
| * To retrieve an instance of this class, call AndroidSyncSettings.get(someContext). |
| - * |
| - * All new public methods MUST call notifyObservers at the end. |
| */ |
| @ThreadSafe |
| public class AndroidSyncSettings { |
| - /** |
| - * In-memory holder of the sync configurations for a given account. On each |
| - * access, updates the cache if the account has changed. This lazy-updating |
| - * model is appropriate as the account changes rarely but may not be known |
| - * when initially constructed. So long as we keep a single account, no |
| - * expensive calls to Android are made. |
| - */ |
| - @NotThreadSafe |
| - @VisibleForTesting |
| - public static class CachedAccountSyncSettings { |
| - private final String mContractAuthority; |
| - private final SyncContentResolverDelegate mSyncContentResolverDelegate; |
| - private Account mAccount; |
| - private boolean mDidUpdate; |
| - private boolean mSyncAutomatically; |
| - private int mIsSyncable; |
| - |
| - public CachedAccountSyncSettings(String contractAuthority, |
| - SyncContentResolverDelegate contentResolverWrapper) { |
| - mContractAuthority = contractAuthority; |
| - mSyncContentResolverDelegate = contentResolverWrapper; |
| - } |
| - |
| - private void ensureSettingsAreForAccount(Account account) { |
| - assert account != null; |
| - if (account.equals(mAccount)) return; |
| - updateSyncSettingsForAccount(account); |
| - mDidUpdate = true; |
| - } |
| - |
| - public void clearUpdateStatus() { |
| - mDidUpdate = false; |
| - } |
| - |
| - public boolean getDidUpdateStatus() { |
| - return mDidUpdate; |
| - } |
| - |
| - // Calling this method may have side-effects. |
| - public boolean getSyncAutomatically(Account account) { |
| - ensureSettingsAreForAccount(account); |
| - return mSyncAutomatically; |
| - } |
| - |
| - public void updateSyncSettingsForAccount(Account account) { |
| - if (account == null) return; |
| - updateSyncSettingsForAccountInternal(account); |
| - } |
| - |
| - public void setIsSyncable(Account account) { |
| - ensureSettingsAreForAccount(account); |
| - if (mIsSyncable == 1) return; |
| - setIsSyncableInternal(account); |
| - } |
| - |
| - public void setSyncAutomatically(Account account, boolean value) { |
| - ensureSettingsAreForAccount(account); |
| - if (mSyncAutomatically == value) return; |
| - setSyncAutomaticallyInternal(account, value); |
| - } |
| - |
| - @VisibleForTesting |
| - protected void updateSyncSettingsForAccountInternal(Account account) { |
| - // Null check here otherwise Findbugs complains. |
| - if (account == null) return; |
| - |
| - boolean oldSyncAutomatically = mSyncAutomatically; |
| - int oldIsSyncable = mIsSyncable; |
| - Account oldAccount = mAccount; |
| - |
| - mAccount = account; |
| - |
| - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| - mSyncAutomatically = mSyncContentResolverDelegate.getSyncAutomatically( |
| - account, mContractAuthority); |
| - mIsSyncable = mSyncContentResolverDelegate.getIsSyncable(account, mContractAuthority); |
| - StrictMode.setThreadPolicy(oldPolicy); |
| - mDidUpdate = (oldIsSyncable != mIsSyncable) |
| - || (oldSyncAutomatically != mSyncAutomatically) |
| - || (!account.equals(oldAccount)); |
| - } |
| - |
| - @VisibleForTesting |
| - protected void setIsSyncableInternal(Account account) { |
| - mIsSyncable = 1; |
| - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| - mSyncContentResolverDelegate.setIsSyncable(account, mContractAuthority, 1); |
| - StrictMode.setThreadPolicy(oldPolicy); |
| - mDidUpdate = true; |
| - } |
| - |
| - @VisibleForTesting |
| - protected void setSyncAutomaticallyInternal(Account account, boolean value) { |
| - mSyncAutomatically = value; |
| - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| - mSyncContentResolverDelegate.setSyncAutomatically(account, mContractAuthority, value); |
| - StrictMode.setThreadPolicy(oldPolicy); |
| - mDidUpdate = true; |
| - } |
| - } |
| - |
| public static final String TAG = "AndroidSyncSettings"; |
| - /** |
| - * Lock for ensuring singleton instantiation across threads. |
| - */ |
| - private static final Object INSTANCE_LOCK = new Object(); |
| + // Lock for ensuring singleton instantiation across threads. |
| + private static final Object LOCK = new Object(); |
| private static AndroidSyncSettings sAndroidSyncSettings; |
| @@ -146,10 +39,13 @@ public class AndroidSyncSettings { |
| private final SyncContentResolverDelegate mSyncContentResolverDelegate; |
| - private boolean mCachedMasterSyncAutomatically; |
| + private Account mAccount = null; |
| + |
| + private boolean mIsSyncable = false; |
| + |
| + private boolean mChromeSyncEnabled = false; |
| - // Instantiation of AndroidSyncSettings is guarded by a lock so volatile is unneeded. |
| - private CachedAccountSyncSettings mCachedSettings; |
| + private boolean mMasterSyncEnabled = false; |
| private final ObserverList<AndroidSyncSettingsObserver> mObservers = |
| new ObserverList<AndroidSyncSettingsObserver>(); |
| @@ -162,125 +58,49 @@ public class AndroidSyncSettings { |
| } |
| /** |
| - * @param context the context |
| - * @param syncContentResolverDelegate an implementation of {@link SyncContentResolverDelegate}. |
| - */ |
| - private AndroidSyncSettings(Context context, |
| - SyncContentResolverDelegate syncContentResolverDelegate, |
| - CachedAccountSyncSettings cachedAccountSettings) { |
| - mApplicationContext = context.getApplicationContext(); |
| - mSyncContentResolverDelegate = syncContentResolverDelegate; |
| - mContractAuthority = getContractAuthority(); |
| - mCachedSettings = cachedAccountSettings; |
| - |
| - updateMasterSyncAutomaticallySetting(); |
| - |
| - mSyncContentResolverDelegate.addStatusChangeListener( |
| - ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, |
| - new AndroidSyncSettingsChangedObserver()); |
| - } |
| - |
| - private void updateMasterSyncAutomaticallySetting() { |
| - StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| - synchronized (mCachedSettings) { |
| - mCachedMasterSyncAutomatically = mSyncContentResolverDelegate |
| - .getMasterSyncAutomatically(); |
| - } |
| - StrictMode.setThreadPolicy(oldPolicy); |
| - } |
| - |
| - /** |
| * A factory method for the AndroidSyncSettings. |
| * |
| * It is possible to override the {@link SyncContentResolverDelegate} to use in tests for the |
| - * instance of the AndroidSyncSettings by calling overrideAndroidSyncSettingsForTests(...) with |
| + * instance of the AndroidSyncSettings by calling overrideForTests(...) with |
| * your {@link SyncContentResolverDelegate}. |
| * |
| * @param context the ApplicationContext is retrieved from the context used as an argument. |
| * @return a singleton instance of the AndroidSyncSettings |
| */ |
| public static AndroidSyncSettings get(Context context) { |
| - synchronized (INSTANCE_LOCK) { |
| + synchronized (LOCK) { |
| if (sAndroidSyncSettings == null) { |
| - SyncContentResolverDelegate contentResolverDelegate = |
| + SyncContentResolverDelegate contentResolver = |
| new SystemSyncContentResolverDelegate(); |
| - CachedAccountSyncSettings cache = new CachedAccountSyncSettings( |
| - context.getPackageName(), contentResolverDelegate); |
| - sAndroidSyncSettings = new AndroidSyncSettings( |
| - context, contentResolverDelegate, cache); |
| + sAndroidSyncSettings = new AndroidSyncSettings(context, contentResolver); |
| } |
| } |
| return sAndroidSyncSettings; |
| } |
| - /** |
| - * Tests might want to consider overriding the context and {@link SyncContentResolverDelegate} |
| - * so they do not use the real ContentResolver in Android. |
| - * |
| - * @param context the context to use |
| - * @param syncContentResolverDelegate the {@link SyncContentResolverDelegate} to use |
| - */ |
| @VisibleForTesting |
| - public static void overrideAndroidSyncSettingsForTests(Context context, |
| - SyncContentResolverDelegate syncContentResolverDelegate, |
| - CachedAccountSyncSettings cachedAccountSettings) { |
| - synchronized (INSTANCE_LOCK) { |
| - if (sAndroidSyncSettings != null) { |
| - throw new IllegalStateException("AndroidSyncSettings already exists"); |
| - } |
| - sAndroidSyncSettings = new AndroidSyncSettings(context, syncContentResolverDelegate, |
| - cachedAccountSettings); |
| + public static void overrideForTests(Context context, |
| + SyncContentResolverDelegate contentResolver) { |
| + synchronized (LOCK) { |
| + sAndroidSyncSettings = new AndroidSyncSettings(context, contentResolver); |
| } |
| } |
| - @VisibleForTesting |
| - public static void overrideAndroidSyncSettingsForTests(Context context, |
| - SyncContentResolverDelegate syncContentResolverDelegate) { |
| - CachedAccountSyncSettings cachedAccountSettings = new CachedAccountSyncSettings( |
| - context.getPackageName(), syncContentResolverDelegate); |
| - overrideAndroidSyncSettingsForTests(context, syncContentResolverDelegate, |
| - cachedAccountSettings); |
| - } |
| - |
| /** |
| - * Returns the contract authority to use when requesting sync. |
| - */ |
| - public String getContractAuthority() { |
| - return mApplicationContext.getPackageName(); |
| - } |
| - |
| - /** |
| - * Add a new AndroidSyncSettingsObserver. |
| - */ |
| - public void registerObserver(AndroidSyncSettingsObserver observer) { |
| - mObservers.addObserver(observer); |
| - } |
| - |
| - /** |
| - * Remove an AndroidSyncSettingsObserver that was previously added. |
| + * @param context the context |
| + * @param syncContentResolverDelegate an implementation of {@link SyncContentResolverDelegate}. |
| */ |
| - public void unregisterObserver(AndroidSyncSettingsObserver observer) { |
| - mObservers.removeObserver(observer); |
| - } |
| + private AndroidSyncSettings(Context context, |
| + SyncContentResolverDelegate syncContentResolverDelegate) { |
| + mApplicationContext = context.getApplicationContext(); |
| + mSyncContentResolverDelegate = syncContentResolverDelegate; |
| + mContractAuthority = getContractAuthority(); |
| - /** |
| - * Checks whether sync is currently enabled from Chrome for a given account. |
| - * |
| - * It checks both the master sync for the device, and Chrome sync setting for the given account. |
| - * |
| - * @param account the account to check if Chrome sync is enabled on. |
| - * @return true if sync is on, false otherwise |
| - */ |
| - public boolean isSyncEnabled(Account account) { |
| - if (account == null) return false; |
| - boolean returnValue; |
| - synchronized (mCachedSettings) { |
| - returnValue = mCachedMasterSyncAutomatically |
| - && mCachedSettings.getSyncAutomatically(account); |
| - } |
| + updateCachedSettings(); |
| - notifyObserversIfAccountSettingsChanged(); |
| - return returnValue; |
| + mSyncContentResolverDelegate.addStatusChangeListener( |
| + ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, |
| + new AndroidSyncSettingsChangedObserver()); |
| } |
| /** |
| @@ -292,7 +112,7 @@ public class AndroidSyncSettings { |
| * @return true if sync is on, false otherwise |
| */ |
| public boolean isSyncEnabled() { |
| - return isSyncEnabled(ChromeSigninController.get(mApplicationContext).getSignedInUser()); |
| + return mMasterSyncEnabled && mChromeSyncEnabled; |
| } |
| /** |
| @@ -304,76 +124,94 @@ public class AndroidSyncSettings { |
| * @param account the account to check if Chrome sync is enabled on. |
| * @return true if sync is on, false otherwise |
| */ |
| - public boolean isChromeSyncEnabled(Account account) { |
| - if (account == null) return false; |
| - |
| - boolean returnValue; |
| - synchronized (mCachedSettings) { |
| - returnValue = mCachedSettings.getSyncAutomatically(account); |
| - } |
| - |
| - notifyObserversIfAccountSettingsChanged(); |
| - return returnValue; |
| + public boolean isChromeSyncEnabled() { |
| + return mChromeSyncEnabled; |
| } |
| /** |
| - * Checks whether the master sync flag for Android is currently set. |
| - * |
| - * @return true if the global master sync is on, false otherwise |
| + * Checks whether the master sync flag for Android is currently enabled. |
| */ |
| public boolean isMasterSyncEnabled() { |
| - synchronized (mCachedSettings) { |
| - return mCachedMasterSyncAutomatically; |
| - } |
| + return mMasterSyncEnabled; |
| } |
| /** |
| * Make sure Chrome is syncable, and enable sync. |
| - * |
| - * @param account the account to enable sync on |
| */ |
| - public void enableChromeSync(Account account) { |
| - makeSyncable(account); |
| + public void enableChromeSync() { |
| + makeSyncable(); |
| + setChromeSyncEnabled(true); |
|
nyquist
2015/02/06 03:03:56
This class is marked as @ThreadSafe. Is it still s
maxbogue
2015/02/10 09:20:00
makeSyncable is now ensureSyncable and is called f
|
| + } |
| + |
| + /** |
| + * Disables Android Chrome sync |
| + */ |
| + public void disableChromeSync() { |
| + setChromeSyncEnabled(false); |
| + } |
| - synchronized (mCachedSettings) { |
| - mCachedSettings.setSyncAutomatically(account, true); |
| + /** |
| + * Must be called when a new account is signed in. |
| + */ |
| + public void updateAccount(Account account) { |
| + mAccount = account; |
| + if (updateCachedSettings()) { |
| + notifyObservers(); |
| } |
| + } |
| - notifyObserversIfAccountSettingsChanged(); |
| + /** |
| + * Returns the contract authority to use when requesting sync. |
| + */ |
| + public String getContractAuthority() { |
| + return mApplicationContext.getPackageName(); |
| } |
| /** |
| - * Disables Android Chrome sync |
| - * |
| - * @param account the account to disable Chrome sync on |
| + * Add a new AndroidSyncSettingsObserver. |
| */ |
| - public void disableChromeSync(Account account) { |
| - synchronized (mCachedSettings) { |
| - mCachedSettings.setSyncAutomatically(account, false); |
| - } |
| + public void registerObserver(AndroidSyncSettingsObserver observer) { |
| + mObservers.addObserver(observer); |
|
nyquist
2015/02/06 03:03:56
I believe ObserverList is marked @NotThreadSafe, b
maxbogue
2015/02/10 09:20:00
Done for register and unregister.
|
| + } |
| + |
| + /** |
| + * Remove an AndroidSyncSettingsObserver that was previously added. |
| + */ |
| + public void unregisterObserver(AndroidSyncSettingsObserver observer) { |
| + mObservers.removeObserver(observer); |
| + } |
| + |
| + private synchronized void setChromeSyncEnabled(boolean value) { |
|
nyquist
2015/02/06 03:03:56
This synchronizes on the instance of this object,
maxbogue
2015/02/10 09:20:00
Done.
|
| + if (value == mChromeSyncEnabled || mAccount == null) return; |
| + mChromeSyncEnabled = value; |
| + |
| + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| + mSyncContentResolverDelegate.setSyncAutomatically(mAccount, mContractAuthority, value); |
| + StrictMode.setThreadPolicy(oldPolicy); |
| - notifyObserversIfAccountSettingsChanged(); |
| + notifyObservers(); |
| } |
| /** |
| - * Register with Android Sync Manager. This is what causes the "Chrome" option to appear in |
| - * Settings -> Accounts / Sync . |
| + * Register Chrome with the Android Sync Manager. |
| * |
| - * @param account the account to enable Chrome sync on |
| + * This is what causes the "Chrome" option to appear in Settings -> Accounts / Sync . |
| */ |
| - private void makeSyncable(Account account) { |
| - synchronized (mCachedSettings) { |
| - mCachedSettings.setIsSyncable(account); |
| - } |
| + private synchronized void makeSyncable() { |
| + if (mIsSyncable || mAccount == null) return; |
| + |
| + mIsSyncable = true; |
| StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| + mSyncContentResolverDelegate.setIsSyncable(mAccount, mContractAuthority, 1); |
| + |
| // Disable the syncability of Chrome for all other accounts. Don't use |
| // our cache as we're touching many accounts that aren't signed in, so this saves |
| // extra calls to Android sync configuration. |
| Account[] googleAccounts = AccountManagerHelper.get(mApplicationContext) |
| .getGoogleAccounts(); |
| for (Account accountToSetNotSyncable : googleAccounts) { |
| - if (!accountToSetNotSyncable.equals(account) |
| + if (!accountToSetNotSyncable.equals(mAccount) |
| && mSyncContentResolverDelegate.getIsSyncable( |
| accountToSetNotSyncable, mContractAuthority) > 0) { |
| mSyncContentResolverDelegate.setIsSyncable(accountToSetNotSyncable, |
| @@ -391,37 +229,40 @@ public class AndroidSyncSettings { |
| private class AndroidSyncSettingsChangedObserver implements SyncStatusObserver { |
| @Override |
| public void onStatusChanged(int which) { |
| - if (ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS == which) { |
| - // Sync settings have changed; update our in-memory caches |
| - synchronized (mCachedSettings) { |
| - mCachedSettings.updateSyncSettingsForAccount( |
| - ChromeSigninController.get(mApplicationContext).getSignedInUser()); |
| - } |
| - |
| - boolean oldMasterSyncEnabled = isMasterSyncEnabled(); |
| - updateMasterSyncAutomaticallySetting(); |
| - boolean didMasterSyncChanged = oldMasterSyncEnabled != isMasterSyncEnabled(); |
| - // Notify observers if MasterSync or account level settings change. |
| - if (didMasterSyncChanged || getAndClearDidUpdateStatus()) { |
| + if (which == ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS) { |
| + // Sync settings have changed; update our cached values. |
| + if (updateCachedSettings()) { |
| + // If something actually changed, tell our observers. |
| notifyObservers(); |
| } |
| } |
| } |
| } |
| - private boolean getAndClearDidUpdateStatus() { |
| - boolean didGetStatusUpdate; |
| - synchronized (mCachedSettings) { |
| - didGetStatusUpdate = mCachedSettings.getDidUpdateStatus(); |
| - mCachedSettings.clearUpdateStatus(); |
| - } |
| - return didGetStatusUpdate; |
| - } |
| + /** |
| + * Update the three cached settings from the content resolver. |
| + * |
| + * @return Whether either chromeSyncEnabled or masterSyncEnabled changed. |
| + */ |
| + private synchronized boolean updateCachedSettings() { |
| + boolean oldChromeSyncEnabled = mChromeSyncEnabled; |
| + boolean oldMasterSyncEnabled = mMasterSyncEnabled; |
| - private void notifyObserversIfAccountSettingsChanged() { |
| - if (getAndClearDidUpdateStatus()) { |
| - notifyObservers(); |
| + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); |
| + if (mAccount != null) { |
| + mIsSyncable = mSyncContentResolverDelegate.getIsSyncable( |
| + mAccount, mContractAuthority) == 1; |
| + mChromeSyncEnabled = mSyncContentResolverDelegate.getSyncAutomatically( |
| + mAccount, mContractAuthority); |
| + } else { |
| + mIsSyncable = false; |
| + mChromeSyncEnabled = false; |
| } |
| + mMasterSyncEnabled = mSyncContentResolverDelegate.getMasterSyncAutomatically(); |
| + StrictMode.setThreadPolicy(oldPolicy); |
| + |
| + return oldChromeSyncEnabled != mChromeSyncEnabled |
| + || oldMasterSyncEnabled != mMasterSyncEnabled; |
| } |
| private void notifyObservers() { |
| @@ -429,4 +270,21 @@ public class AndroidSyncSettings { |
| observer.androidSyncSettingsChanged(); |
| } |
| } |
| + |
| + @Deprecated |
| + @VisibleForTesting |
| + public static void overrideAndroidSyncSettingsForTests(Context context, |
| + SyncContentResolverDelegate contentResolver) { |
| + overrideForTests(context, contentResolver); |
| + } |
| + |
| + @Deprecated |
| + public boolean isChromeSyncEnabled(Account account) { |
| + return isChromeSyncEnabled(); |
| + } |
| + |
| + @Deprecated |
| + public boolean isSyncEnabled(Account account) { |
| + return isSyncEnabled(); |
| + } |
| } |