diff options
| author | Sandra Kwan <sandrakwan@google.com> | 2015-12-04 23:35:20 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-12-04 23:35:20 +0000 |
| commit | db9df39992ce6a1a09c2f26cde639baf126fa6f5 (patch) | |
| tree | d1bfd55738c54cb72f39225221d0916776cc98de /core/java/android | |
| parent | 12701f64d93c4b2e11b09b30f7dc9dc96d51150f (diff) | |
| parent | 920f6ef983024c15fbd47f7be7fa9204559f2514 (diff) | |
Merge "AccountManager: add finishSession(...) API."
Diffstat (limited to 'core/java/android')
4 files changed, 259 insertions, 28 deletions
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index 041f591e0ba8..a312e3f5b6a7 100644 --- a/core/java/android/accounts/AbstractAccountAuthenticator.java +++ b/core/java/android/accounts/AbstractAccountAuthenticator.java @@ -18,6 +18,7 @@ package android.accounts; import android.os.Bundle; import android.os.RemoteException; +import android.text.TextUtils; import android.os.Binder; import android.os.IBinder; import android.content.pm.PackageManager; @@ -121,24 +122,28 @@ public abstract class AbstractAccountAuthenticator { * This is used in the default implementation of * {@link #startAddAccountSession} and {@link startUpdateCredentialsSession}. */ - private static final String KEY_AUTH_TOKEN_TYPE = "android.accounts.KEY_AUTH_TOKEN_TYPE"; + private static final String KEY_AUTH_TOKEN_TYPE = + "android.accounts.AbstractAccountAuthenticato.KEY_AUTH_TOKEN_TYPE"; /** * Bundle key used for the {@link String} array of required features in * session bundle. This is used in the default implementation of * {@link #startAddAccountSession} and {@link startUpdateCredentialsSession}. */ - private static final String KEY_REQUIRED_FEATURES = "android.accounts.AbstractAccountAuthenticator.KEY_REQUIRED_FEATURES"; + private static final String KEY_REQUIRED_FEATURES = + "android.accounts.AbstractAccountAuthenticator.KEY_REQUIRED_FEATURES"; /** * Bundle key used for the {@link Bundle} options in session bundle. This is * used in default implementation of {@link #startAddAccountSession} and * {@link startUpdateCredentialsSession}. */ - private static final String KEY_OPTIONS = "android.accounts.AbstractAccountAuthenticator.KEY_OPTIONS"; + private static final String KEY_OPTIONS = + "android.accounts.AbstractAccountAuthenticator.KEY_OPTIONS"; /** * Bundle key used for the {@link Account} account in session bundle. This is used * used in default implementation of {@link startUpdateCredentialsSession}. */ - private static final String KEY_ACCOUNT = "android.accounts.AbstractAccountAuthenticator.KEY_ACCOUNT"; + private static final String KEY_ACCOUNT = + "android.accounts.AbstractAccountAuthenticator.KEY_ACCOUNT"; private final Context mContext; @@ -418,6 +423,7 @@ public abstract class AbstractAccountAuthenticator { } Log.v(TAG, "startUpdateCredentialsSession: result " + AccountManager.sanitizeResult(result)); + } if (result != null) { response.onResult(result); @@ -425,6 +431,33 @@ public abstract class AbstractAccountAuthenticator { } catch (Exception e) { handleException(response, "startUpdateCredentialsSession", account.toString() + "," + authTokenType, e); + + } + } + + public void finishSession( + IAccountAuthenticatorResponse response, + String accountType, + Bundle sessionBundle) throws RemoteException { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "finishSession: accountType " + accountType); + } + checkBinderPermission(); + try { + final Bundle result = AbstractAccountAuthenticator.this.finishSession( + new AccountAuthenticatorResponse(response), accountType, sessionBundle); + if (result != null) { + result.keySet(); // force it to be unparcelled + } + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "finishSession: result " + AccountManager.sanitizeResult(result)); + } + if (result != null) { + response.onResult(result); + } + } catch (Exception e) { + handleException(response, "finishSession", accountType, e); + } } } @@ -697,7 +730,13 @@ public abstract class AbstractAccountAuthenticator { /** * Starts the add account session to authenticate user to an account of the - * specified accountType. + * specified accountType. No file I/O should be performed in this call. + * Account should be added to device only when {@link #finishSession} is + * called after this. + * <p> + * Note: when overriding this method, {@link #finishSession} should be + * overridden too. + * </p> * * @param response to send the result back to the AccountManager, will never * be null @@ -722,9 +761,13 @@ public abstract class AbstractAccountAuthenticator { * </ul> * @throws NetworkErrorException if the authenticator could not honor the * request due to a network error + * @see #finishSession(AccountAuthenticatorResponse, String, Bundle) */ - public Bundle startAddAccountSession(final AccountAuthenticatorResponse response, - final String accountType, final String authTokenType, final String[] requiredFeatures, + public Bundle startAddAccountSession( + final AccountAuthenticatorResponse response, + final String accountType, + final String authTokenType, + final String[] requiredFeatures, final Bundle options) throws NetworkErrorException { new Thread(new Runnable() { @@ -744,34 +787,43 @@ public abstract class AbstractAccountAuthenticator { } /** - * Asks user to re-authenticate for an account but defers updating the locally stored - * credentials. + * Asks user to re-authenticate for an account but defers updating the + * locally stored credentials. No file I/O should be performed in this call. + * Local credentials should be updated only when {@link #finishSession} is + * called after this. + * <p> + * Note: when overriding this method, {@link #finishSession} should be + * overridden too. + * </p> * * @param response to send the result back to the AccountManager, will never * be null * @param account the account whose credentials are to be updated, will * never be null * @param authTokenType the type of auth token to retrieve after updating - * the credentials, may be null (TODO) + * the credentials, may be null * @param options a Bundle of authenticator-specific options, may be null * @return a Bundle result or null if the result is to be returned via the * response. The result will contain either: * <ul> * <li>{@link AccountManager#KEY_INTENT}, or - * <li>{@link AccountManager#KEY_ACCOUNT_SESSION_BUNDLE} for updating the - * locally stored credentials later, and if account is - * re-authenticated, {@link AccountManager#KEY_PASSWORD} and - * {@link AccountManager#KEY_ACCOUNT_STATUS_TOKEN} for checking the - * status of the account later, or + * <li>{@link AccountManager#KEY_ACCOUNT_SESSION_BUNDLE} for + * updating the locally stored credentials later, and if account is + * re-authenticated, optional {@link AccountManager#KEY_PASSWORD} + * and {@link AccountManager#KEY_ACCOUNT_STATUS_TOKEN} for checking + * the status of the account later, or * <li>{@link AccountManager#KEY_ERROR_CODE} and * {@link AccountManager#KEY_ERROR_MESSAGE} to indicate an error * </ul> * @throws NetworkErrorException if the authenticator could not honor the * request due to a network error + * @see #finishSession(AccountAuthenticatorResponse, String, Bundle) */ - public Bundle startUpdateCredentialsSession(final AccountAuthenticatorResponse response, - final Account account, final String authTokenType, final Bundle options) - throws NetworkErrorException { + public Bundle startUpdateCredentialsSession( + final AccountAuthenticatorResponse response, + final Account account, + final String authTokenType, + final Bundle options) throws NetworkErrorException { new Thread(new Runnable() { @Override public void run() { @@ -787,4 +839,102 @@ public abstract class AbstractAccountAuthenticator { }).start(); return null; } + + /** + * Finishes the session started by #startAddAccountSession or + * #startUpdateCredentials by installing the account to device with + * AccountManager, or updating the local credentials. File I/O may be + * performed in this call. + * <p> + * Note: when overriding this method, {@link #startAddAccountSession} and + * {@link #startUpdateCredentialsSession} should be overridden too. + * </p> + * + * @param response to send the result back to the AccountManager, will never + * be null + * @param accountType the type of account to authenticate with, will never + * be null + * @param sessionBundle a bundle of session data created by + * {@link #startAddAccountSession} used for adding account to + * device, or by {@link #startUpdateCredentialsSession} used for + * updating local credentials. + * @return a Bundle result or null if the result is to be returned via the + * response. The result will contain either: + * <ul> + * <li>{@link AccountManager#KEY_INTENT}, or + * <li>{@link AccountManager#KEY_ACCOUNT_NAME} and + * {@link AccountManager#KEY_ACCOUNT_TYPE} of the account that was + * added or local credentials were updated, or + * <li>{@link AccountManager#KEY_ERROR_CODE} and + * {@link AccountManager#KEY_ERROR_MESSAGE} to indicate an error + * </ul> + * @throws NetworkErrorException + * @see #startAddAccountSession and #startUpdateCredentialsSession + */ + public Bundle finishSession( + final AccountAuthenticatorResponse response, + final String accountType, + final Bundle sessionBundle) throws NetworkErrorException { + if (TextUtils.isEmpty(accountType)) { + Log.e(TAG, "Account type cannot be empty."); + Bundle result = new Bundle(); + result.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_BAD_ARGUMENTS); + result.putString(AccountManager.KEY_ERROR_MESSAGE, + "accountType cannot be empty."); + return result; + } + + if (sessionBundle == null) { + Log.e(TAG, "Session bundle cannot be null."); + Bundle result = new Bundle(); + result.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_BAD_ARGUMENTS); + result.putString(AccountManager.KEY_ERROR_MESSAGE, + "sessionBundle cannot be null."); + return result; + } + + if (!sessionBundle.containsKey(KEY_AUTH_TOKEN_TYPE)) { + // We cannot handle Session bundle not created by default startAddAccountSession(...) + // nor startUpdateCredentialsSession(...) implementation. Return error. + Bundle result = new Bundle(); + result.putInt(AccountManager.KEY_ERROR_CODE, + AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION); + result.putString(AccountManager.KEY_ERROR_MESSAGE, + "Authenticator must override finishSession if startAddAccountSession" + + " or startUpdateCredentialsSession is overridden."); + response.onResult(result); + return result; + } + String authTokenType = sessionBundle.getString(KEY_AUTH_TOKEN_TYPE); + Bundle options = sessionBundle.getBundle(KEY_OPTIONS); + String[] requiredFeatures = sessionBundle.getStringArray(KEY_REQUIRED_FEATURES); + Account account = sessionBundle.getParcelable(KEY_ACCOUNT); + boolean containsKeyAccount = sessionBundle.containsKey(KEY_ACCOUNT); + + // Actual options passed to add account or update credentials flow. + Bundle sessionOptions = new Bundle(sessionBundle); + // Remove redundant extras in session bundle before passing it to addAccount(...) or + // updateCredentials(...). + sessionOptions.remove(KEY_AUTH_TOKEN_TYPE); + sessionOptions.remove(KEY_REQUIRED_FEATURES); + sessionOptions.remove(KEY_OPTIONS); + sessionOptions.remove(KEY_ACCOUNT); + + if (options != null) { + // options may contains old system info such as + // AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or update + // credentials flow, we should replace with the new values of the current call added + // to sessionBundle by AccountManager or AccountManagerService. + options.putAll(sessionOptions); + sessionOptions = options; + } + + // Session bundle created by startUpdateCredentialsSession default implementation should + // contain KEY_ACCOUNT. + if (containsKeyAccount) { + return updateCredentials(response, account, authTokenType, options); + } + // Otherwise, session bundle was created by startAddAccountSession default implementation. + return addAccount(response, accountType, authTokenType, requiredFeatures, sessionOptions); + } } diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 5557905e4f47..ada1ac268fc0 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -2617,7 +2617,8 @@ public class AccountManager { * <p> * <p> * <b>NOTE:</b> The account will not be installed to the device by calling - * this api alone. + * this api alone. #finishSession should be called after this to install the + * account on device. * * @param accountType The type of account to add; must not be null * @param authTokenType The type of auth token (see {@link #getAuthToken}) @@ -2665,6 +2666,7 @@ public class AccountManager { * problem creating a new account, usually because of network * trouble * </ul> + * @see #finishSession */ public AccountManagerFuture<Bundle> startAddAccountSession( final String accountType, @@ -2697,14 +2699,14 @@ public class AccountManager { /** * Asks the user to enter a new password for an account but not updating the - * saved credentials for the account until finishSession is - * called. + * saved credentials for the account until {@link #finishSession} is called. * <p> * This method may be called from any thread, but the returned * {@link AccountManagerFuture} must not be used on the main thread. * <p> * <b>NOTE:</b> The saved credentials for the account alone will not be - * updated by calling this API alone . + * updated by calling this API alone. #finishSession should be called after + * this to update local credentials * * @param account The account to update credentials for * @param authTokenType The credentials entered must allow an auth token of @@ -2723,15 +2725,14 @@ public class AccountManager { * the main thread * @return An {@link AccountManagerFuture} which resolves to a Bundle with * these fields if an activity was supplied and user was - * successfully re-authenticated to the account (TODO: default impl - * only returns escorw?): + * successfully re-authenticated to the account: * <ul> * <li>{@link #KEY_ACCOUNT_SESSION_BUNDLE} - encrypted Bundle for * updating the local credentials on device later. - * <li>{@link #KEY_PASSWORD} - optional, the password or password hash of the - * account - * <li>{@link #KEY_ACCOUNT_STATUS_TOKEN} - optional, token to check status of - * the account + * <li>{@link #KEY_PASSWORD} - optional, the password or password + * hash of the account + * <li>{@link #KEY_ACCOUNT_STATUS_TOKEN} - optional, token to check + * status of the account * </ul> * If no activity was specified, the returned Bundle contains * {@link #KEY_INTENT} with the {@link Intent} needed to launch the @@ -2747,6 +2748,7 @@ public class AccountManager { * problem verifying the password, usually because of network * trouble * </ul> + * @see #finishSession */ public AccountManagerFuture<Bundle> startUpdateCredentialsSession( final Account account, @@ -2770,4 +2772,71 @@ public class AccountManager { } }.start(); } + + /** + * Finishes the session started by {@link #startAddAccountSession} or + * {@link #startUpdateCredentialsSession}. This will either add the account + * to AccountManager or update the local credentials stored. + * <p> + * This method may be called from any thread, but the returned + * {@link AccountManagerFuture} must not be used on the main thread. + * + * @param sessionBundle a {@link Bundle} created by {@link #startAddAccountSession} or + * {@link #startUpdateCredentialsSession} + * @param activity The {@link Activity} context to use for launching a new + * authenticator-defined sub-Activity to prompt the user to + * create an account or reauthenticate existing account; used + * only to call startActivity(); if null, the prompt will not + * be launched directly, but the necessary {@link Intent} will + * be returned to the caller instead + * @param callback Callback to invoke when the request completes, null for + * no callback + * @param handler {@link Handler} identifying the callback thread, null for + * the main thread + * @return An {@link AccountManagerFuture} which resolves to a Bundle with + * these fields if an activity was supplied and an account was added + * to device or local credentials were updated:: + * <ul> + * <li>{@link #KEY_ACCOUNT_NAME} - the name of the account created + * <li>{@link #KEY_ACCOUNT_TYPE} - the type of the account + * </ul> + * If no activity was specified and additional information is needed + * from user, the returned Bundle may contains only + * {@link #KEY_INTENT} with the {@link Intent} needed to launch the + * actual account creation process. If an error occurred, + * {@link AccountManagerFuture#getResult()} throws: + * <ul> + * <li>{@link AuthenticatorException} if no authenticator was + * registered for this account type or the authenticator failed to + * respond + * <li>{@link OperationCanceledException} if the operation was + * canceled for any reason, including the user canceling the + * creation process or adding accounts (of this type) has been + * disabled by policy + * <li>{@link IOException} if the authenticator experienced an I/O + * problem creating a new account, usually because of network + * trouble + * </ul> + * @see #startAddAccountSession and #startUpdateCredentialsSession + */ + public AccountManagerFuture<Bundle> finishSession( + final Bundle sessionBundle, + final Activity activity, + AccountManagerCallback<Bundle> callback, + Handler handler) { + if (sessionBundle == null) { + throw new IllegalArgumentException("sessionBundle is null"); + } + + /* Add information required by add account flow */ + final Bundle appInfo = new Bundle(); + appInfo.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName()); + + return new AmsTask(activity, handler, callback) { + @Override + public void doWork() throws RemoteException { + mService.finishSession(mResponse, sessionBundle, activity != null, appInfo); + } + }.start(); + } } diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl index 921fb19ddb63..6bda80056b6f 100644 --- a/core/java/android/accounts/IAccountAuthenticator.aidl +++ b/core/java/android/accounts/IAccountAuthenticator.aidl @@ -96,4 +96,12 @@ oneway interface IAccountAuthenticator { */ void startUpdateCredentialsSession(in IAccountAuthenticatorResponse response, in Account account, String authTokenType, in Bundle options); + + /** + * Finishes the session started by startAddAccountSession(...) or + * startUpdateCredentialsSession(...) by adding account to or updating local credentials + * in the IAccountManager. + */ + void finishSession(in IAccountAuthenticatorResponse response, String accountType, + in Bundle sessionBundle); } diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl index 8489e47c0ba4..4af9f3325dff 100644 --- a/core/java/android/accounts/IAccountManager.aidl +++ b/core/java/android/accounts/IAccountManager.aidl @@ -91,4 +91,8 @@ interface IAccountManager { /* Update credentials in two steps. */ void startUpdateCredentialsSession(in IAccountManagerResponse response, in Account account, String authTokenType, boolean expectActivityLaunch, in Bundle options); + + /* Finish session started by startAddAccountSession(...) or startUpdateCredentialsSession(...) */ + void finishSession(in IAccountManagerResponse response, in Bundle sessionBundle, + boolean expectActivityLaunch, in Bundle appInfo); } |
