diff options
author | dsmyers@chromium.org <dsmyers@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-24 22:17:40 +0000 |
---|---|---|
committer | dsmyers@chromium.org <dsmyers@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-24 22:17:40 +0000 |
commit | 44d5119a63db796e1fd99c7af9119237076b4e78 (patch) | |
tree | a894bd1ce4751ca0288ba2a9bd449bdf1a19c033 /sync | |
parent | 5b29403640e6f7b45d27db1825af78e0fd4bf29e (diff) | |
download | chromium_src-44d5119a63db796e1fd99c7af9119237076b4e78.zip chromium_src-44d5119a63db796e1fd99c7af9119237076b4e78.tar.gz chromium_src-44d5119a63db796e1fd99c7af9119237076b4e78.tar.bz2 |
Upstreams SyncStatusHelper and dependencies.
Upstreams SyncStatusHelper, AccountManagerHelper,
SharedPreferencesUtil, and files on which they depened. These changes
are required to ultimately upstream the sync notification client.
BUG=159221
Review URL: https://chromiumcodereview.appspot.com/11929040
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@178670 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync')
7 files changed, 849 insertions, 0 deletions
diff --git a/sync/android/java/src/org/chromium/sync/notifier/SyncContentResolverDelegate.java b/sync/android/java/src/org/chromium/sync/notifier/SyncContentResolverDelegate.java new file mode 100644 index 0000000..d0e3edc --- /dev/null +++ b/sync/android/java/src/org/chromium/sync/notifier/SyncContentResolverDelegate.java @@ -0,0 +1,33 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.notifier; + + +import android.accounts.Account; +import android.content.SyncStatusObserver; + +/** + * Since the ContentResolver in Android has a lot of static methods, it is hard to + * mock out for tests. This interface wraps all the sync-related methods we use from + * the Android ContentResolver. + */ +public interface SyncContentResolverDelegate { + + Object addStatusChangeListener(int mask, SyncStatusObserver callback); + + void removeStatusChangeListener(Object handle); + + void setMasterSyncAutomatically(boolean sync); + + boolean getMasterSyncAutomatically(); + + void setSyncAutomatically(Account account, String authority, boolean sync); + + boolean getSyncAutomatically(Account account, String authority); + + void setIsSyncable(Account account, String authority, int syncable); + + int getIsSyncable(Account account, String authority); +} diff --git a/sync/android/java/src/org/chromium/sync/notifier/SyncStatusHelper.java b/sync/android/java/src/org/chromium/sync/notifier/SyncStatusHelper.java new file mode 100644 index 0000000..2239d5c --- /dev/null +++ b/sync/android/java/src/org/chromium/sync/notifier/SyncStatusHelper.java @@ -0,0 +1,337 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.notifier; + + +import android.accounts.Account; +import android.content.ContentResolver; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SyncStatusObserver; +import android.os.StrictMode; +import android.preference.PreferenceManager; +import android.util.Log; + +import org.chromium.sync.signin.AccountManagerHelper; + +import com.google.common.annotations.VisibleForTesting; + +import java.util.ArrayList; + +/** + * A helper class to handle the current status of sync for Chrome in Android-land. + * + * It also has a helper class to be used by observers whenever sync settings change. + * + * To retrieve an instance of this class, call SyncStatusHelper.get(someContext). + */ +public class SyncStatusHelper { + + public interface Listener { + /** + * Called when the user signs out of Chrome. + */ + void onClearSignedInUser(); + } + + @VisibleForTesting + public static final String SIGNED_IN_ACCOUNT_KEY = "google.services.username"; + + public static final String TAG = "SyncStatusHelper"; + + private final Context mApplicationContext; + + private final SyncContentResolverDelegate mSyncContentResolverWrapper; + + private static final Object lock = new Object(); + + private static SyncStatusHelper sSyncStatusHelper; + + private ArrayList<Listener> mListeners; + + /** + * @param context the context + * @param syncContentResolverWrapper an implementation of SyncContentResolverWrapper + */ + private SyncStatusHelper(Context context, + SyncContentResolverDelegate syncContentResolverWrapper) { + mApplicationContext = context.getApplicationContext(); + mSyncContentResolverWrapper = syncContentResolverWrapper; + mListeners = new ArrayList<Listener>(); + } + + /** + * A factory method for the SyncStatusHelper. + * + * It is possible to override the SyncContentResolverWrapper to use in tests for the + * instance of the SyncStatusHelper by calling overrideSyncStatusHelperForTests(...) with + * your SyncContentResolverWrapper. + * + * @param context the ApplicationContext is retreived from the context used as an argument. + * @return a singleton instance of the SyncStatusHelper + */ + public static SyncStatusHelper get(Context context) { + synchronized (lock) { + if (sSyncStatusHelper == null) { + Context applicationContext = context.getApplicationContext(); + sSyncStatusHelper = new SyncStatusHelper(applicationContext, + new SystemSyncContentResolverDelegate()); + } + } + return sSyncStatusHelper; + } + + /** + * Tests might want to consider overriding the context and SyncContentResolverWrapper so they + * do not use the real ContentResolver in Android. + * + * @param context the context to use + * @param syncContentResolverWrapper the SyncContentResolverWrapper to use + */ + @VisibleForTesting + public static void overrideSyncStatusHelperForTests(Context context, + SyncContentResolverDelegate syncContentResolverWrapper) { + synchronized (lock) { + if (sSyncStatusHelper != null) { + throw new IllegalStateException("SyncStatusHelper already exists"); + } + sSyncStatusHelper = new SyncStatusHelper(context, syncContentResolverWrapper); + } + } + + /** + * Wrapper method for the ContentResolver.addStatusChangeListener(...) when we are only + * interested in the settings type. + */ + public Object registerContentResolverObserver(SyncStatusObserver observer) { + return mSyncContentResolverWrapper.addStatusChangeListener( + ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, observer); + } + + /** + * Wrapper method for the ContentResolver.removeStatusChangeListener(...). + */ + public void unregisterContentResolverObserver(Object observerHandle) { + mSyncContentResolverWrapper.removeStatusChangeListener(observerHandle); + } + + /** + * 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) { + StrictMode.ThreadPolicy oldPolicy = temporarilyAllowDiskWritesAndDiskReads(); + String contractAuthority = + InvalidationController.newInstance(mApplicationContext).getContractAuthority(); + boolean enabled = account != null && + mSyncContentResolverWrapper.getMasterSyncAutomatically() && + mSyncContentResolverWrapper.getSyncAutomatically(account, contractAuthority); + StrictMode.setThreadPolicy(oldPolicy); + return enabled; + } + + /** + * Checks whether sync is currently enabled from Chrome for the currently signed in account. + * + * It checks both the master sync for the device, and Chrome sync setting for the given account. + * If no user is currently signed in it returns false. + * + * @return true if sync is on, false otherwise + */ + public boolean isSyncEnabled() { + return isSyncEnabled(getSignedInUser()); + } + + /** + * Checks whether sync is currently enabled from Chrome for a given account. + * + * It checks only Chrome sync setting for the given account, + * and ignores the master sync setting. + * + * @param account the account to check if Chrome sync is enabled on. + * @return true if sync is on, false otherwise + */ + public boolean isSyncEnabledForChrome(Account account) { + StrictMode.ThreadPolicy oldPolicy = temporarilyAllowDiskWritesAndDiskReads(); + String contractAuthority = + InvalidationController.newInstance(mApplicationContext).getContractAuthority(); + boolean enabled = account != null && + mSyncContentResolverWrapper.getSyncAutomatically(account, contractAuthority); + StrictMode.setThreadPolicy(oldPolicy); + return enabled; + } + + /** + * Checks whether the master sync flag for Android is currently set. + * + * @return true if the global master sync is on, false otherwise + */ + public boolean isMasterSyncAutomaticallyEnabled() { + StrictMode.ThreadPolicy oldPolicy = temporarilyAllowDiskWritesAndDiskReads(); + boolean enabled = mSyncContentResolverWrapper.getMasterSyncAutomatically(); + StrictMode.setThreadPolicy(oldPolicy); + return enabled; + } + + /** + * Make sure Chrome is syncable, and enable sync. + * + * @param account the account to enable sync on + */ + public void enableAndroidSync(Account account) { + StrictMode.ThreadPolicy oldPolicy = temporarilyAllowDiskWritesAndDiskReads(); + makeSyncable(account); + String contractAuthority = + InvalidationController.newInstance(mApplicationContext).getContractAuthority(); + if (!mSyncContentResolverWrapper.getSyncAutomatically(account, contractAuthority)) { + mSyncContentResolverWrapper.setSyncAutomatically(account, contractAuthority, true); + } + StrictMode.setThreadPolicy(oldPolicy); + } + + /** + * Disables Android Chrome sync + * + * @param account the account to disable Chrome sync on + */ + public void disableAndroidSync(Account account) { + StrictMode.ThreadPolicy oldPolicy = temporarilyAllowDiskWritesAndDiskReads(); + String contractAuthority = + InvalidationController.newInstance(mApplicationContext).getContractAuthority(); + if (mSyncContentResolverWrapper.getSyncAutomatically(account, contractAuthority)) { + mSyncContentResolverWrapper.setSyncAutomatically(account, contractAuthority, false); + } + StrictMode.setThreadPolicy(oldPolicy); + } + + public Account getSignedInUser() { + String syncAccountName = getSignedInAccountName(); + if (syncAccountName == null) { + return null; + } + return AccountManagerHelper.createAccountFromName(syncAccountName); + } + + public boolean isSignedIn() { + return getSignedInAccountName() != null; + } + + public void setSignedInAccountName(String accountName) { + getPreferences().edit() + .putString(SIGNED_IN_ACCOUNT_KEY, accountName) + .apply(); + } + + public void clearSignedInUser() { + Log.d(TAG, "Clearing user signed in to Chrome"); + setSignedInAccountName(null); + + for (Listener listener : mListeners) { + listener.onClearSignedInUser(); + } + } + + private String getSignedInAccountName() { + return getPreferences().getString(SIGNED_IN_ACCOUNT_KEY, null); + } + + /** + * Register with Android Sync Manager. This is what causes the "Chrome" option to appear in + * Settings -> Accounts / Sync . + * + * @param account the account to enable Chrome sync on + */ + private void makeSyncable(Account account) { + String contractAuthority = + InvalidationController.newInstance(mApplicationContext).getContractAuthority(); + if (hasFinishedFirstSync(account)) { + mSyncContentResolverWrapper.setIsSyncable(account, contractAuthority, 1); + } + // Disable the syncability of Chrome for all other accounts + Account[] googleAccounts = AccountManagerHelper.get(mApplicationContext). + getGoogleAccounts(); + for (Account accountToSetNotSyncable : googleAccounts) { + if (!accountToSetNotSyncable.equals(account) && + mSyncContentResolverWrapper.getIsSyncable( + accountToSetNotSyncable, contractAuthority) > 0) { + mSyncContentResolverWrapper.setIsSyncable(accountToSetNotSyncable, + contractAuthority, 0); + } + } + } + + /** + * Returns whether the given account has ever been synced. + */ + boolean hasFinishedFirstSync(Account account) { + String contractAuthority = + InvalidationController.newInstance(mApplicationContext).getContractAuthority(); + return mSyncContentResolverWrapper.getIsSyncable(account, contractAuthority) <= 0; + } + + /** + * Helper class to be used by observers whenever sync settings change. + * + * To register the observer, call SyncStatusHelper.registerObserver(...). + */ + public static abstract class SyncSettingsChangedObserver implements SyncStatusObserver { + + @Override + public void onStatusChanged(int which) { + if (ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS == which) { + syncSettingsChanged(); + } + } + + protected abstract void syncSettingsChanged(); + } + + /** + * Returns the default shared preferences. + */ + private SharedPreferences getPreferences() { + return PreferenceManager.getDefaultSharedPreferences(mApplicationContext); + } + + /** + * Sets a new StrictMode.ThreadPolicy based on the current one, but allows disk reads + * and disk writes. + * + * The return value is the old policy, which must be applied after the disk access is finished, + * by using StrictMode.setThreadPolicy(oldPolicy). + * + * @return the policy before allowing reads and writes. + */ + private static StrictMode.ThreadPolicy temporarilyAllowDiskWritesAndDiskReads() { + StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); + StrictMode.ThreadPolicy.Builder newPolicy = + new StrictMode.ThreadPolicy.Builder(oldPolicy); + newPolicy.permitDiskReads(); + newPolicy.permitDiskWrites(); + StrictMode.setThreadPolicy(newPolicy.build()); + return oldPolicy; + } + + /** + * Adds a Listener. + * @param listener Listener to add. + */ + public void addListener(Listener listener) { + mListeners.add(listener); + } + + /** + * Removes a Listener. + * @param listener Listener to remove from the list. + * @returns whether or not the Listener was removed. + */ + public boolean removeListener(Listener listener) { + return mListeners.remove(listener); + } +} diff --git a/sync/android/java/src/org/chromium/sync/notifier/SystemSyncContentResolverDelegate.java b/sync/android/java/src/org/chromium/sync/notifier/SystemSyncContentResolverDelegate.java new file mode 100644 index 0000000..ce00c8c --- /dev/null +++ b/sync/android/java/src/org/chromium/sync/notifier/SystemSyncContentResolverDelegate.java @@ -0,0 +1,53 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.notifier; + + +import android.accounts.Account; +import android.content.ContentResolver; +import android.content.SyncStatusObserver; + +public class SystemSyncContentResolverDelegate implements SyncContentResolverDelegate { + + @Override + public Object addStatusChangeListener(int mask, SyncStatusObserver callback) { + return ContentResolver.addStatusChangeListener(mask, callback); + } + + @Override + public void removeStatusChangeListener(Object handle) { + ContentResolver.removeStatusChangeListener(handle); + } + + @Override + public void setMasterSyncAutomatically(boolean sync) { + ContentResolver.setMasterSyncAutomatically(sync); + } + + @Override + public boolean getMasterSyncAutomatically() { + return ContentResolver.getMasterSyncAutomatically(); + } + + @Override + public boolean getSyncAutomatically(Account account, String authority) { + return ContentResolver.getSyncAutomatically(account, authority); + } + + @Override + public void setSyncAutomatically(Account account, String authority, boolean sync) { + ContentResolver.setSyncAutomatically(account, authority, sync); + } + + @Override + public void setIsSyncable(Account account, String authority, int syncable) { + ContentResolver.setIsSyncable(account, authority, syncable); + } + + @Override + public int getIsSyncable(Account account, String authority) { + return ContentResolver.getIsSyncable(account, authority); + } +} diff --git a/sync/android/java/src/org/chromium/sync/signin/AccountManagerDelegate.java b/sync/android/java/src/org/chromium/sync/signin/AccountManagerDelegate.java new file mode 100644 index 0000000..88ec210 --- /dev/null +++ b/sync/android/java/src/org/chromium/sync/signin/AccountManagerDelegate.java @@ -0,0 +1,52 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.signin; + +import android.accounts.Account; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.app.Activity; +import android.os.Bundle; +import android.os.Handler; + +import java.io.IOException; + +/** + * Wrapper around the Android account manager, to facilitate dependency injection during testing. + */ +public interface AccountManagerDelegate { + Account[] getAccountsByType(String type); + + AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType, + boolean notifyAuthFailure, AccountManagerCallback<Bundle> callback, Handler handler); + + AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType, Bundle options, + Activity activity, AccountManagerCallback<Bundle> callback, Handler handler); + + void invalidateAuthToken(String accountType, String authToken); + + String blockingGetAuthToken(Account account, String authTokenType, boolean notifyAuthFailure) + throws OperationCanceledException, IOException, AuthenticatorException; + + Account[] getAccounts(); + + boolean addAccountExplicitly(Account account, String password, Bundle userdata); + + AccountManagerFuture<Boolean> removeAccount(Account account, + AccountManagerCallback<Boolean> callback, Handler handler); + + String getPassword(Account account); + + void setPassword(Account account, String password); + + void clearPassword(Account account); + + AccountManagerFuture<Bundle> confirmCredentials(Account account, Bundle bundle, + Activity activity, AccountManagerCallback<Bundle> callback, Handler handler); + + String peekAuthToken(Account account, String authTokenType); +} diff --git a/sync/android/java/src/org/chromium/sync/signin/AccountManagerHelper.java b/sync/android/java/src/org/chromium/sync/signin/AccountManagerHelper.java new file mode 100644 index 0000000..889b194 --- /dev/null +++ b/sync/android/java/src/org/chromium/sync/signin/AccountManagerHelper.java @@ -0,0 +1,225 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.signin; + + +import com.google.common.annotations.VisibleForTesting; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.util.Log; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * AccountManagerHelper wraps our access of AccountManager in Android. + * + * Use the AccountManagerHelper.get(someContext) to instantiate it + */ +public class AccountManagerHelper { + + private static final String TAG = "AccountManagerHelper"; + + private static final String GOOGLE_ACCOUNT_TYPE = "com.google"; + + private static final Object lock = new Object(); + + private static AccountManagerHelper sAccountManagerHelper; + + private final AccountManagerDelegate mAccountManager; + + private Context mApplicationContext; + + public interface GetAuthTokenCallback { + /** + * Invoked on the UI thread once a token has been provided by the AccountManager. + * @param token Auth token, or null if no token is available (bad credentials, + * permission denied, etc). + */ + void tokenAvailable(String token); + } + + /** + * @param context the Android context + * @param accountManager the account manager to use as a backend service + */ + private AccountManagerHelper(Context context, + AccountManagerDelegate accountManager) { + mApplicationContext = context.getApplicationContext(); + mAccountManager = accountManager; + } + + /** + * A factory method for the AccountManagerHelper. + * + * It is possible to override the AccountManager to use in tests for the instance of the + * AccountManagerHelper by calling overrideAccountManagerHelperForTests(...) with + * your MockAccountManager. + * + * @param context the applicationContext is retrieved from the context used as an argument. + * @return a singleton instance of the AccountManagerHelper + */ + public static AccountManagerHelper get(Context context) { + synchronized (lock) { + if (sAccountManagerHelper == null) { + sAccountManagerHelper = new AccountManagerHelper(context, + new SystemAccountManagerDelegate(context)); + } + } + return sAccountManagerHelper; + } + + @VisibleForTesting + public static void overrideAccountManagerHelperForTests(Context context, + AccountManagerDelegate accountManager) { + synchronized (lock) { + sAccountManagerHelper = new AccountManagerHelper(context, accountManager); + } + } + + /** + * Creates an Account object for the given name. + */ + public static Account createAccountFromName(String name) { + return new Account(name, GOOGLE_ACCOUNT_TYPE); + } + + public List<String> getGoogleAccountNames() { + List<String> accountNames = new ArrayList<String>(); + Account[] accounts = mAccountManager.getAccountsByType(GOOGLE_ACCOUNT_TYPE); + for (Account account : accounts) { + accountNames.add(account.name); + } + return accountNames; + } + + public Account[] getGoogleAccounts() { + return mAccountManager.getAccountsByType(GOOGLE_ACCOUNT_TYPE); + } + + public boolean hasGoogleAccounts() { + return getGoogleAccounts().length > 0; + } + + /** + * Returns the account if it exists, null otherwise. + */ + public Account getAccountFromName(String accountName) { + Account[] accounts = mAccountManager.getAccountsByType(GOOGLE_ACCOUNT_TYPE); + for (Account account : accounts) { + if (account.name.equals(accountName)) { + return account; + } + } + return null; + } + + /** + * Gets the auth token synchronously. + * + * - Assumes that the account is a valid account. + * - Should not be called on the main thread. + */ + public String getAuthTokenFromBackground(Account account, String authTokenType) { + try { + AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, + authTokenType, false, null, null); + Bundle result = future.getResult(); + if (result == null) { + Log.w(TAG, "Auth token - getAuthToken returned null"); + return null; + } + if (result.containsKey(AccountManager.KEY_INTENT)) { + Log.d(TAG, "Starting intent to get auth credentials"); + // Need to start intent to get credentials + Intent intent = result.getParcelable(AccountManager.KEY_INTENT); + int flags = intent.getFlags(); + flags |= Intent.FLAG_ACTIVITY_NEW_TASK; + intent.setFlags(flags); + mApplicationContext.startActivity(intent); + return null; + } + return result.getString(AccountManager.KEY_AUTHTOKEN); + } catch (OperationCanceledException e) { + Log.w(TAG, "Auth token - operation cancelled", e); + } catch (AuthenticatorException e) { + Log.w(TAG, "Auth token - authenticator exception", e); + } catch (IOException e) { + Log.w(TAG, "Auth token - IO exception", e); + } + return null; + } + + /** + * Gets the auth token and returns the response asynchronously on the UI thread. + * This should be called when we have a foreground activity that needs an auth token. + * + * - Assumes that the account is a valid account. + */ + public void getAuthTokenFromForeground(Activity activity, Account account, + String authTokenType, final GetAuthTokenCallback callback) { + final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, + authTokenType, null, activity, null, null); + new AsyncTask<Void, Void, String>() { + @Override + public String doInBackground(Void... params) { + try { + Bundle result = future.getResult(); + if (result != null) { + return result.getString(AccountManager.KEY_AUTHTOKEN); + } else { + Log.w(TAG, "Auth token - getAuthToken returned null"); + } + } catch (OperationCanceledException e) { + Log.w(TAG, "Auth token - operation cancelled", e); + } catch (AuthenticatorException e) { + Log.w(TAG, "Auth token - authenticator exception", e); + } catch (IOException e) { + Log.w(TAG, "Auth token - IO exception", e); + } + return null; + } + @Override + public void onPostExecute(String authToken) { + callback.tokenAvailable(authToken); + } + }.execute(); + } + + /** + * Invalidates the old token (if non-null/non-empty) and synchronously generates a new one. + * Also notifies the user (via status bar) if any user action is required. The method will + * return null if any user action is required to generate the new token. + * + * - Assumes that the account is a valid account. + * - Should not be called on the main thread. + */ + public String getNewAuthToken(Account account, String authToken, String authTokenType) { + if (authToken != null && !authToken.isEmpty()) { + mAccountManager.invalidateAuthToken(GOOGLE_ACCOUNT_TYPE, authToken); + } + + try { + return mAccountManager.blockingGetAuthToken(account, authTokenType, true); + } catch (OperationCanceledException e) { + Log.w(TAG, "Auth token - operation cancelled", e); + } catch (AuthenticatorException e) { + Log.w(TAG, "Auth token - authenticator exception", e); + } catch (IOException e) { + Log.w(TAG, "Auth token - IO exception", e); + } + return null; + } +} diff --git a/sync/android/java/src/org/chromium/sync/signin/SystemAccountManagerDelegate.java b/sync/android/java/src/org/chromium/sync/signin/SystemAccountManagerDelegate.java new file mode 100644 index 0000000..13e3962 --- /dev/null +++ b/sync/android/java/src/org/chromium/sync/signin/SystemAccountManagerDelegate.java @@ -0,0 +1,105 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.signin; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; + +import java.io.IOException; + +/** + * Default implementation of {@link AccountManagerDelegate} which delegates all calls to the + * Android account manager. + */ +public class SystemAccountManagerDelegate implements AccountManagerDelegate { + + private final AccountManager mAccountManager; + + public SystemAccountManagerDelegate(Context context) { + mAccountManager = AccountManager.get(context); + } + + @Override + public Account[] getAccountsByType(String type) { + return mAccountManager.getAccountsByType(type); + } + + @Override + public AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType, + boolean notifyAuthFailure, AccountManagerCallback<Bundle> callback, Handler handler) { + return mAccountManager.getAuthToken(account, authTokenType, notifyAuthFailure, callback, + handler); + } + + @Override + public AccountManagerFuture<Bundle> getAuthToken(Account account, String authTokenType, + Bundle options, Activity activity, AccountManagerCallback<Bundle> callback, + Handler handler) { + return mAccountManager.getAuthToken(account, authTokenType, options, activity, callback, + handler); + } + + @Override + public void invalidateAuthToken(String accountType, String authToken) { + mAccountManager.invalidateAuthToken(accountType, authToken); + } + + @Override + public String blockingGetAuthToken(Account account, String authTokenType, + boolean notifyAuthFailure) + throws OperationCanceledException, IOException, AuthenticatorException { + return mAccountManager.blockingGetAuthToken(account, authTokenType, notifyAuthFailure); + } + + @Override + public Account[] getAccounts() { + return mAccountManager.getAccounts(); + } + + @Override + public boolean addAccountExplicitly(Account account, String password, Bundle userdata) { + return mAccountManager.addAccountExplicitly(account, password, userdata); + } + + @Override + public AccountManagerFuture<Boolean> removeAccount(Account account, + AccountManagerCallback<Boolean> callback, Handler handler) { + return mAccountManager.removeAccount(account, callback, handler); + } + + @Override + public String getPassword(Account account) { + return mAccountManager.getPassword(account); + } + + @Override + public void setPassword(Account account, String password) { + mAccountManager.setPassword(account, password); + } + + @Override + public void clearPassword(Account account) { + mAccountManager.clearPassword(account); + } + + @Override + public AccountManagerFuture<Bundle> confirmCredentials(Account account, Bundle bundle, + Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) { + return mAccountManager.confirmCredentials(account, bundle, activity, callback, handler); + } + + @Override + public String peekAuthToken(Account account, String authTokenType) { + return mAccountManager.peekAuthToken(account, authTokenType); + } +} diff --git a/sync/android/javatests/src/org/chromium/sync/notifier/signin/SyncStatusHelperTest.java b/sync/android/javatests/src/org/chromium/sync/notifier/signin/SyncStatusHelperTest.java new file mode 100644 index 0000000..69f3528 --- /dev/null +++ b/sync/android/javatests/src/org/chromium/sync/notifier/signin/SyncStatusHelperTest.java @@ -0,0 +1,44 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.notifier.signin; + +import android.content.ContentResolver; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.base.test.util.Feature; +import org.chromium.sync.notifier.SyncStatusHelper; + +public class SyncStatusHelperTest extends InstrumentationTestCase { + + @SmallTest + @Feature({"Sync"}) + public void testStatusChangeForSettingsShouldCauseCall() { + TestSyncSettingsChangedObserver observer = new TestSyncSettingsChangedObserver(); + observer.onStatusChanged(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + assertTrue("Should have called the observer", observer.gotCalled); + } + + @SmallTest + @Feature({"Sync"}) + public void testStatusChangeForAnythingElseShouldNoCauseCall() { + TestSyncSettingsChangedObserver observer = new TestSyncSettingsChangedObserver(); + observer.onStatusChanged(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE); + observer.onStatusChanged(ContentResolver.SYNC_OBSERVER_TYPE_PENDING); + assertFalse("Should not have called the observer", observer.gotCalled); + } + + private static class TestSyncSettingsChangedObserver + extends SyncStatusHelper.SyncSettingsChangedObserver { + + public boolean gotCalled; + + @Override + protected void syncSettingsChanged() { + gotCalled = true; + } + } + +} |