/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.accounts; import android.app.Activity; import android.content.Intent; import android.content.Context; import android.content.IntentFilter; import android.content.BroadcastReceiver; import android.database.SQLException; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.Parcelable; import android.util.Log; import java.io.IOException; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeUnit; import java.util.HashMap; import java.util.Map; import com.google.android.collect.Maps; /** * A class that helps with interactions with the AccountManager Service. It provides * methods to allow for account, password, and authtoken management for all accounts on the * device. One accesses the {@link AccountManager} by calling: *
* AccountManager accountManager = AccountManager.get(context); ** *
* The AccountManager Service provides storage for the accounts known to the system, * provides methods to manage them, and allows the registration of authenticators to * which operations such as addAccount and getAuthToken are delegated. *
* Many of the calls take an {@link AccountManagerCallback} and {@link Handler} as parameters. * These calls return immediately but run asynchronously. If a callback is provided then * {@link AccountManagerCallback#run} will be invoked wen the request completes, successfully * or not. An {@link AccountManagerFuture} is returned by these requests and also passed into the * callback. The result if retrieved by calling {@link AccountManagerFuture#getResult()} which * either returns the result or throws an exception as appropriate. *
* The asynchronous request can be made blocking by not providing a callback and instead * calling {@link AccountManagerFuture#getResult()} on the future that is returned. This will * cause the running thread to block until the result is returned. Keep in mind that one * should not block the main thread in this way. Instead one should either use a callback, * thus making the call asynchronous, or make the blocking call on a separate thread. *
* If one wants to ensure that the callback is invoked from a specific handler then they should * pass the handler to the request. This makes it easier to ensure thread-safety by running * all of one's logic from a single handler. */ public class AccountManager { private static final String TAG = "AccountManager"; public static final int ERROR_CODE_REMOTE_EXCEPTION = 1; public static final int ERROR_CODE_NETWORK_ERROR = 3; public static final int ERROR_CODE_CANCELED = 4; public static final int ERROR_CODE_INVALID_RESPONSE = 5; public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6; public static final int ERROR_CODE_BAD_ARGUMENTS = 7; public static final int ERROR_CODE_BAD_REQUEST = 8; public static final String KEY_ACCOUNTS = "accounts"; public static final String KEY_AUTHENTICATOR_TYPES = "authenticator_types"; public static final String KEY_USERDATA = "userdata"; public static final String KEY_AUTHTOKEN = "authtoken"; public static final String KEY_PASSWORD = "password"; public static final String KEY_ACCOUNT_NAME = "authAccount"; public static final String KEY_ACCOUNT_TYPE = "accountType"; public static final String KEY_ERROR_CODE = "errorCode"; public static final String KEY_ERROR_MESSAGE = "errorMessage"; public static final String KEY_INTENT = "intent"; public static final String KEY_BOOLEAN_RESULT = "booleanResult"; public static final String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "accountAuthenticatorResponse"; public static final String KEY_ACCOUNT_MANAGER_RESPONSE = "accountManagerResponse"; public static final String KEY_AUTH_FAILED_MESSAGE = "authFailedMessage"; public static final String KEY_AUTH_TOKEN_LABEL = "authTokenLabelKey"; public static final String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator"; public static final String AUTHENTICATOR_META_DATA_NAME = "android.accounts.AccountAuthenticator"; public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator"; private final Context mContext; private final IAccountManager mService; private final Handler mMainHandler; /** * Action sent as a broadcast Intent by the AccountsService * when accounts are added to and/or removed from the device's * database. */ public static final String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED"; /** * @hide */ public AccountManager(Context context, IAccountManager service) { mContext = context; mService = service; mMainHandler = new Handler(mContext.getMainLooper()); } /** * @hide used for testing only */ public AccountManager(Context context, IAccountManager service, Handler handler) { mContext = context; mService = service; mMainHandler = handler; } /** * Retrieve an AccountManager instance that is associated with the context that is passed in. * Certain calls such as {@link #addOnAccountsUpdatedListener} use this context internally, * so the caller must take care to use a {@link Context} whose lifetime is associated with * the listener registration. * @param context The {@link Context} to use when necessary * @return an {@link AccountManager} instance that is associated with context */ public static AccountManager get(Context context) { return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); } /** * Get the password that is associated with the account. Returns null if the account does * not exist. *
* Requires that the caller has permission * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running * with the same UID as the Authenticator for the account. */ public String getPassword(final Account account) { try { return mService.getPassword(account); } catch (RemoteException e) { // will never happen throw new RuntimeException(e); } } /** * Get the user data named by "key" that is associated with the account. * Returns null if the account does not exist or if it does not have a value for key. *
* Requires that the caller has permission * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running * with the same UID as the Authenticator for the account. */ public String getUserData(final Account account, final String key) { try { return mService.getUserData(account, key); } catch (RemoteException e) { // will never happen throw new RuntimeException(e); } } /** * Query the AccountManager Service for an array that contains a * {@link AuthenticatorDescription} for each registered authenticator. * @return an array that contains all the authenticators known to the AccountManager service. * This array will be empty if there are no authenticators and will never return null. *
* No permission is required to make this call. */ public AuthenticatorDescription[] getAuthenticatorTypes() { try { return mService.getAuthenticatorTypes(); } catch (RemoteException e) { // will never happen throw new RuntimeException(e); } } /** * Query the AccountManager Service for all accounts. * @return an array that contains all the accounts known to the AccountManager service. * This array will be empty if there are no accounts and will never return null. *
* Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS} */ public Account[] getAccounts() { try { return mService.getAccounts(null); } catch (RemoteException e) { // won't ever happen throw new RuntimeException(e); } } /** * Query the AccountManager for the set of accounts that have a given type. If null * is passed as the type than all accounts are returned. * @param type the account type by which to filter, or null to get all accounts * @return an array that contains the accounts that match the specified type. This array * will be empty if no accounts match. It will never return null. *
* Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS} */ public Account[] getAccountsByType(String type) { try { return mService.getAccounts(type); } catch (RemoteException e) { // won't ever happen throw new RuntimeException(e); } } /** * Tests that the given account has the specified features. If this account does not exist * then this call returns false. *
* This call returns immediately but runs asynchronously and the result is accessed via the * {@link AccountManagerFuture} that is returned. This future is also passed as the sole * parameter to the {@link AccountManagerCallback}. If the caller wished to use this * method asynchronously then they will generally pass in a callback object that will get * invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then * they will generally pass null for the callback and instead call * {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value, * which will then block until the request completes. *
* Requires that the caller has permission {@link android.Manifest.permission#GET_ACCOUNTS}.
*
* @param account The {@link Account} to test
* @param features the features for which to test
* @param callback A callback to invoke when the request completes. If null then
* no callback is invoked.
* @param handler The {@link Handler} to use to invoke the callback. If null then the
* main thread's {@link Handler} is used.
* @return an {@link AccountManagerFuture} that represents the future result of the call.
* The future result is a {@link Boolean} that is true if the account exists and has the
* specified features.
*/
public AccountManagerFuture
* Requires that the caller has permission
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
* with the same UID as the Authenticator for the account.
* @param account The account to add
* @param password The password to associate with the account. May be null.
* @param userdata A bundle of key/value pairs to set as the account's userdata. May be null.
* @return true if the account was sucessfully added, false otherwise, for example,
* if the account already exists or if the account is null
*/
public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
try {
return mService.addAccount(account, password, userdata);
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
}
}
/**
* Removes the given account. If this account does not exist then this call has no effect.
*
* This call returns immediately but runs asynchronously and the result is accessed via the
* {@link AccountManagerFuture} that is returned. This future is also passed as the sole
* parameter to the {@link AccountManagerCallback}. If the caller wished to use this
* method asynchronously then they will generally pass in a callback object that will get
* invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
* they will generally pass null for the callback and instead call
* {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
* which will then block until the request completes.
*
* Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
*
* @param account The {@link Account} to remove
* @param callback A callback to invoke when the request completes. If null then
* no callback is invoked.
* @param handler The {@link Handler} to use to invoke the callback. If null then the
* main thread's {@link Handler} is used.
* @return an {@link AccountManagerFuture} that represents the future result of the call.
* The future result is a {@link Boolean} that is true if the account is successfully removed
* or false if the authenticator refuses to remove the account.
*/
public AccountManagerFuture
* Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
* @param accountType the account type of the authtoken to invalidate
* @param authToken the authtoken to invalidate
*/
public void invalidateAuthToken(final String accountType, final String authToken) {
try {
mService.invalidateAuthToken(accountType, authToken);
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
}
}
/**
* Gets the authtoken named by "authTokenType" for the specified account if it is cached
* by the AccountManager. If no authtoken is cached then null is returned rather than
* asking the authenticaticor to generate one. If the account or the
* authtoken do not exist then null is returned.
*
* Requires that the caller has permission
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
* with the same UID as the Authenticator for the account.
* @param account the account whose authtoken is to be retrieved, must not be null
* @param authTokenType the type of authtoken to retrieve
* @return an authtoken for the given account and authTokenType, if one is cached by the
* AccountManager, null otherwise.
*/
public String peekAuthToken(final Account account, final String authTokenType) {
if (account == null) {
Log.e(TAG, "peekAuthToken: the account must not be null");
return null;
}
if (authTokenType == null) {
return null;
}
try {
return mService.peekAuthToken(account, authTokenType);
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
}
}
/**
* Sets the password for the account. The password may be null. If the account does not exist
* then this call has no affect.
*
* Requires that the caller has permission
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
* with the same UID as the Authenticator for the account.
* @param account the account whose password is to be set. Must not be null.
* @param password the password to set for the account. May be null.
*/
public void setPassword(final Account account, final String password) {
if (account == null) {
Log.e(TAG, "the account must not be null");
return;
}
try {
mService.setPassword(account, password);
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
}
}
/**
* Sets the password for account to null. If the account does not exist then this call
* has no effect.
*
* Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
* @param account the account whose password is to be cleared. Must not be null.
*/
public void clearPassword(final Account account) {
if (account == null) {
Log.e(TAG, "the account must not be null");
return;
}
try {
mService.clearPassword(account);
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
}
}
/**
* Sets account's userdata named "key" to the specified value. If the account does not
* exist then this call has no effect.
*
* Requires that the caller has permission
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
* with the same UID as the Authenticator for the account.
* @param account the account whose userdata is to be set. Must not be null.
* @param key the key of the userdata to set. Must not be null.
* @param value the value to set. May be null.
*/
public void setUserData(final Account account, final String key, final String value) {
if (account == null) {
Log.e(TAG, "the account must not be null");
return;
}
if (key == null) {
Log.e(TAG, "the key must not be null");
return;
}
try {
mService.setUserData(account, key, value);
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
}
}
/**
* Sets the authtoken named by "authTokenType" to the value specified by authToken.
* If the account does not exist then this call has no effect.
*
* Requires that the caller has permission
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and is running
* with the same UID as the Authenticator for the account.
* @param account the account whose authtoken is to be set. Must not be null.
* @param authTokenType the type of the authtoken to set. Must not be null.
* @param authToken the authToken to set. May be null.
*/
public void setAuthToken(Account account, final String authTokenType, final String authToken) {
try {
mService.setAuthToken(account, authTokenType, authToken);
} catch (RemoteException e) {
// won't ever happen
throw new RuntimeException(e);
}
}
/**
* Convenience method that makes a blocking call to
* {@link #getAuthToken(Account, String, boolean, AccountManagerCallback, Handler)}
* then extracts and returns the value of {@link #KEY_AUTHTOKEN} from its result.
*
* Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}.
* @param account the account whose authtoken is to be retrieved, must not be null
* @param authTokenType the type of authtoken to retrieve
* @param notifyAuthFailure if true, cause the AccountManager to put up a "sign-on" notification
* for the account if no authtoken is cached by the AccountManager and the the authenticator
* does not have valid credentials to get an authtoken.
* @return an authtoken for the given account and authTokenType, if one is cached by the
* AccountManager, null otherwise.
* @throws AuthenticatorException if the authenticator is not present, unreachable or returns
* an invalid response.
* @throws OperationCanceledException if the request is canceled for any reason
* @throws java.io.IOException if the authenticator experiences an IOException while attempting
* to communicate with its backend server.
*/
public String blockingGetAuthToken(Account account, String authTokenType,
boolean notifyAuthFailure)
throws OperationCanceledException, IOException, AuthenticatorException {
Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
null /* handler */).getResult();
return bundle.getString(KEY_AUTHTOKEN);
}
/**
* Request that an authtoken of the specified type be returned for an account.
* If the Account Manager has a cached authtoken of the requested type then it will
* service the request itself. Otherwise it will pass the request on to the authenticator.
* The authenticator can try to service this request with information it already has stored
* in the AccountManager but may need to launch an activity to prompt the
* user to enter credentials. If it is able to retrieve the authtoken it will be returned
* in the result.
*
* If the authenticator needs to prompt the user for credentials it will return an intent to
* the activity that will do the prompting. If an activity is supplied then that activity
* will be used to launch the intent and the result will come from it. Otherwise a result will
* be returned that contains the intent.
*
* This call returns immediately but runs asynchronously and the result is accessed via the
* {@link AccountManagerFuture} that is returned. This future is also passed as the sole
* parameter to the {@link AccountManagerCallback}. If the caller wished to use this
* method asynchronously then they will generally pass in a callback object that will get
* invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
* they will generally pass null for the callback and instead call
* {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
* which will then block until the request completes.
*
* Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}.
*
* @param account The account whose credentials are to be updated.
* @param authTokenType the auth token to retrieve as part of updating the credentials.
* May be null.
* @param options authenticator specific options for the request
* @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
* the intent will be started with this activity. If activity is null then the result will
* be returned as-is.
* @param callback A callback to invoke when the request completes. If null then
* no callback is invoked.
* @param handler The {@link Handler} to use to invoke the callback. If null then the
* main thread's {@link Handler} is used.
* @return an {@link AccountManagerFuture} that represents the future result of the call.
* The future result is a {@link Bundle} that contains:
*
* If the authenticator needs to prompt the user for credentials it will return an intent for
* an activity that will do the prompting. If an intent is returned and notifyAuthFailure
* is true then a notification will be created that launches this intent.
*
* This call returns immediately but runs asynchronously and the result is accessed via the
* {@link AccountManagerFuture} that is returned. This future is also passed as the sole
* parameter to the {@link AccountManagerCallback}. If the caller wished to use this
* method asynchronously then they will generally pass in a callback object that will get
* invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
* they will generally pass null for the callback and instead call
* {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
* which will then block until the request completes.
*
* Requires that the caller has permission {@link android.Manifest.permission#USE_CREDENTIALS}.
*
* @param account The account whose credentials are to be updated.
* @param authTokenType the auth token to retrieve as part of updating the credentials.
* May be null.
* @param notifyAuthFailure if true and the authenticator returns a {@link #KEY_INTENT} in the
* result then a "sign-on needed" notification will be created that will launch this intent.
* @param callback A callback to invoke when the request completes. If null then
* no callback is invoked.
* @param handler The {@link Handler} to use to invoke the callback. If null then the
* main thread's {@link Handler} is used.
* @return an {@link AccountManagerFuture} that represents the future result of the call.
* The future result is a {@link Bundle} that contains either:
*
* This call returns immediately but runs asynchronously and the result is accessed via the
* {@link AccountManagerFuture} that is returned. This future is also passed as the sole
* parameter to the {@link AccountManagerCallback}. If the caller wished to use this
* method asynchronously then they will generally pass in a callback object that will get
* invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
* they will generally pass null for the callback and instead call
* {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
* which will then block until the request completes.
*
* Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
*
* @param accountType The type of account to add. This must not be null.
* @param authTokenType The account that is added should be able to service this auth token
* type. This may be null.
* @param requiredFeatures The account that is added should support these features.
* This array may be null or empty.
* @param addAccountOptions A bundle of authenticator-specific options that is passed on
* to the authenticator. This may be null.
* @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
* the intent will be started with this activity. If activity is null then the result will
* be returned as-is.
* @param callback A callback to invoke when the request completes. If null then
* no callback is invoked.
* @param handler The {@link Handler} to use to invoke the callback. If null then the
* main thread's {@link Handler} is used.
* @return an {@link AccountManagerFuture} that represents the future result of the call.
* The future result is a {@link Bundle} that contains either:
*
* This call returns immediately but runs asynchronously and the result is accessed via the
* {@link AccountManagerFuture} that is returned. This future is also passed as the sole
* parameter to the {@link AccountManagerCallback}. If the caller wished to use this
* method asynchronously then they will generally pass in a callback object that will get
* invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
* they will generally pass null for the callback and instead call
* {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
* which will then block until the request completes.
*
* Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
*
* @param account The account whose credentials are to be checked
* @param options authenticator specific options for the request
* @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
* the intent will be started with this activity. If activity is null then the result will
* be returned as-is.
* @param callback A callback to invoke when the request completes. If null then
* no callback is invoked.
* @param handler The {@link Handler} to use to invoke the callback. If null then the
* main thread's {@link Handler} is used.
* @return an {@link AccountManagerFuture} that represents the future result of the call.
* The future result is a {@link Bundle} that contains either:
*
* This call returns immediately but runs asynchronously and the result is accessed via the
* {@link AccountManagerFuture} that is returned. This future is also passed as the sole
* parameter to the {@link AccountManagerCallback}. If the caller wished to use this
* method asynchronously then they will generally pass in a callback object that will get
* invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
* they will generally pass null for the callback and instead call
* {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
* which will then block until the request completes.
*
* Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
*
* @param account The account whose credentials are to be updated.
* @param authTokenType the auth token to retrieve as part of updating the credentials.
* May be null.
* @param options authenticator specific options for the request
* @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
* the intent will be started with this activity. If activity is null then the result will
* be returned as-is.
* @param callback A callback to invoke when the request completes. If null then
* no callback is invoked.
* @param handler The {@link Handler} to use to invoke the callback. If null then the
* main thread's {@link Handler} is used.
* @return an {@link AccountManagerFuture} that represents the future result of the call.
* The future result is a {@link Bundle} that contains either:
*
* This call returns immediately but runs asynchronously and the result is accessed via the
* {@link AccountManagerFuture} that is returned. This future is also passed as the sole
* parameter to the {@link AccountManagerCallback}. If the caller wished to use this
* method asynchronously then they will generally pass in a callback object that will get
* invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
* they will generally pass null for the callback and instead call
* {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
* which will then block until the request completes.
*
* Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
*
* @param accountType The account type of the authenticator whose properties are to be edited.
* @param activity If the authenticator returns a {@link #KEY_INTENT} in the result then
* the intent will be started with this activity. If activity is null then the result will
* be returned as-is.
* @param callback A callback to invoke when the request completes. If null then
* no callback is invoked.
* @param handler The {@link Handler} to use to invoke the callback. If null then the
* main thread's {@link Handler} is used.
* @return an {@link AccountManagerFuture} that represents the future result of the call.
* The future result is a {@link Bundle} that contains either:
*
* This call returns immediately but runs asynchronously and the result is accessed via the
* {@link AccountManagerFuture} that is returned. This future is also passed as the sole
* parameter to the {@link AccountManagerCallback}. If the caller wished to use this
* method asynchronously then they will generally pass in a callback object that will get
* invoked with the {@link AccountManagerFuture}. If they wish to use it synchronously then
* they will generally pass null for the callback and instead call
* {@link android.accounts.AccountManagerFuture#getResult()} on this method's return value,
* which will then block until the request completes.
*
* Requires that the caller has permission {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
*
* @param accountType the accountType to query; this must be non-null
* @param authTokenType the type of authtoken to retrieve; this must be non-null
* @param features a filter for the accounts. See {@link #getAccountsByTypeAndFeatures}.
* @param activityForPrompting The activity used to start any account management
* activities that are required to fulfill this request. This may be null.
* @param addAccountOptions authenticator-specific options used if an account needs to be added
* @param getAuthTokenOptions authenticator-specific options passed to getAuthToken
* @param callback A callback to invoke when the request completes. If null then
* no callback is invoked.
* @param handler The {@link Handler} to use to invoke the callback. If null then the
* main thread's {@link Handler} is used.
* @return an {@link AccountManagerFuture} that represents the future result of the call.
* The future result is a {@link Bundle} that contains either:
*
* You must remove this listener before the context that was used to retrieve this
* {@link AccountManager} instance goes away. This generally means when the Activity
* or Service you are running is stopped.
* @param listener the listener to add
* @param handler the Handler whose thread will be used to invoke the listener. If null
* the AccountManager context's main thread will be used.
* @param updateImmediately if true then the listener will be invoked as a result of this
* call.
* @throws IllegalArgumentException if listener is null
* @throws IllegalStateException if listener was already added
*/
public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener,
Handler handler, boolean updateImmediately) {
if (listener == null) {
throw new IllegalArgumentException("the listener is null");
}
synchronized (mAccountsUpdatedListeners) {
if (mAccountsUpdatedListeners.containsKey(listener)) {
throw new IllegalStateException("this listener is already added");
}
final boolean wasEmpty = mAccountsUpdatedListeners.isEmpty();
mAccountsUpdatedListeners.put(listener, handler);
if (wasEmpty) {
// Register a broadcast receiver to monitor account changes
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
// To recover from disk-full.
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter);
}
}
if (updateImmediately) {
postToHandler(handler, listener, getAccounts());
}
}
/**
* Remove an {@link OnAccountsUpdateListener} that was previously registered with
* {@link #addOnAccountsUpdatedListener}.
* @param listener the listener to remove
* @throws IllegalArgumentException if listener is null
* @throws IllegalStateException if listener was not already added
*/
public void removeOnAccountsUpdatedListener(OnAccountsUpdateListener listener) {
if (listener == null) {
Log.e(TAG, "Missing listener");
return;
}
synchronized (mAccountsUpdatedListeners) {
if (!mAccountsUpdatedListeners.containsKey(listener)) {
Log.e(TAG, "Listener was not previously added");
return;
}
mAccountsUpdatedListeners.remove(listener);
if (mAccountsUpdatedListeners.isEmpty()) {
mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver);
}
}
}
}
*
* If the user presses "back" then the request will be canceled.
*/
public AccountManagerFuture
*
* If the user presses "back" then the request will be canceled.
*/
public AccountManagerFuture
*
*/
public AccountManagerFuture
*
* If the user presses "back" then the request will be canceled.
*/
public AccountManagerFuture
*
* If the user presses "back" then the request will be canceled.
*/
public AccountManagerFuture
*
* If the user presses "back" then the request will be canceled.
*/
public AccountManagerFuture
*
* If the user presses "back" then the request will be canceled.
*/
public AccountManagerFuture