diff options
| -rw-r--r-- | api/current.txt | 2 | ||||
| -rw-r--r-- | api/system-current.txt | 2 | ||||
| -rw-r--r-- | core/java/android/accounts/AbstractAccountAuthenticator.java | 95 | ||||
| -rw-r--r-- | core/java/android/accounts/AccountManager.java | 96 | ||||
| -rw-r--r-- | core/java/android/accounts/IAccountAuthenticator.aidl | 6 | ||||
| -rw-r--r-- | core/java/android/accounts/IAccountManager.aidl | 3 | ||||
| -rw-r--r-- | services/core/java/com/android/server/accounts/AccountManagerService.java | 61 |
7 files changed, 254 insertions, 11 deletions
diff --git a/api/current.txt b/api/current.txt index 5048788cd556..2f5686e3ad15 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2734,6 +2734,7 @@ package android.accounts { method public final android.os.IBinder getIBinder(); method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException; method public android.os.Bundle startAddAccountSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException; + method public android.os.Bundle startUpdateCredentialsSession(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException; method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException; field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry"; } @@ -2799,6 +2800,7 @@ package android.accounts { method public void setPassword(android.accounts.Account, java.lang.String); method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String); method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler); + method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler); method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler); field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator"; field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator"; diff --git a/api/system-current.txt b/api/system-current.txt index ae0ac0ce7b0c..cbe8db7453f0 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -2833,6 +2833,7 @@ package android.accounts { method public final android.os.IBinder getIBinder(); method public abstract android.os.Bundle hasFeatures(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String[]) throws android.accounts.NetworkErrorException; method public android.os.Bundle startAddAccountSession(android.accounts.AccountAuthenticatorResponse, java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle) throws android.accounts.NetworkErrorException; + method public android.os.Bundle startUpdateCredentialsSession(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException; method public abstract android.os.Bundle updateCredentials(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, java.lang.String, android.os.Bundle) throws android.accounts.NetworkErrorException; field public static final java.lang.String KEY_CUSTOM_TOKEN_EXPIRY = "android.accounts.expiry"; } @@ -2898,6 +2899,7 @@ package android.accounts { method public void setPassword(android.accounts.Account, java.lang.String); method public void setUserData(android.accounts.Account, java.lang.String, java.lang.String); method public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler); + method public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler); method public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, java.lang.String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler); field public static final java.lang.String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator"; field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator"; diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index 185ceb430e13..041f591e0ba8 100644 --- a/core/java/android/accounts/AbstractAccountAuthenticator.java +++ b/core/java/android/accounts/AbstractAccountAuthenticator.java @@ -119,21 +119,26 @@ public abstract class AbstractAccountAuthenticator { /** * Bundle key used for the {@link String} account type in session bundle. * This is used in the default implementation of - * {@link #startAddAccountSession}. TODO: and startUpdateCredentialsSession. + * {@link #startAddAccountSession} and {@link startUpdateCredentialsSession}. */ private static final String KEY_AUTH_TOKEN_TYPE = "android.accounts.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}. TODO: and startUpdateCredentialsSession. + * {@link #startAddAccountSession} and {@link startUpdateCredentialsSession}. */ 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}. TODO: - * and startUpdateCredentialsSession. + * used in default implementation of {@link #startAddAccountSession} and + * {@link startUpdateCredentialsSession}. */ 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 final Context mContext; @@ -385,6 +390,43 @@ public abstract class AbstractAccountAuthenticator { handleException(response, "startAddAccountSession", accountType, e); } } + + @Override + public void startUpdateCredentialsSession( + IAccountAuthenticatorResponse response, + Account account, + String authTokenType, + Bundle loginOptions) throws RemoteException { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "startUpdateCredentialsSession: " + + account + + ", authTokenType " + + authTokenType); + } + checkBinderPermission(); + try { + final Bundle result = AbstractAccountAuthenticator.this + .startUpdateCredentialsSession( + new AccountAuthenticatorResponse(response), + account, + authTokenType, + loginOptions); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + // Result may be null. + if (result != null) { + result.keySet(); // force it to be unparcelled + } + Log.v(TAG, "startUpdateCredentialsSession: result " + + AccountManager.sanitizeResult(result)); + } + if (result != null) { + response.onResult(result); + } + } catch (Exception e) { + handleException(response, "startUpdateCredentialsSession", + account.toString() + "," + authTokenType, e); + } + } } private void handleException(IAccountAuthenticatorResponse response, String method, @@ -700,4 +742,49 @@ public abstract class AbstractAccountAuthenticator { }).start(); return null; } + + /** + * Asks user to re-authenticate for an account but defers updating the locally stored + * credentials. + * + * @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) + * @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_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 + */ + 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() { + Bundle sessionBundle = new Bundle(); + sessionBundle.putString(KEY_AUTH_TOKEN_TYPE, authTokenType); + sessionBundle.putParcelable(KEY_ACCOUNT, account); + sessionBundle.putBundle(KEY_OPTIONS, options); + Bundle result = new Bundle(); + result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle); + response.onResult(result); + } + + }).start(); + return null; + } } diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 42e5e2a5fb6e..5557905e4f47 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -2666,9 +2666,14 @@ public class AccountManager { * trouble * </ul> */ - public AccountManagerFuture<Bundle> startAddAccountSession(final String accountType, - final String authTokenType, final String[] requiredFeatures, final Bundle options, - final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) { + public AccountManagerFuture<Bundle> startAddAccountSession( + final String accountType, + final String authTokenType, + final String[] requiredFeatures, + final Bundle options, + final Activity activity, + AccountManagerCallback<Bundle> callback, + Handler handler) { if (accountType == null) throw new IllegalArgumentException("accountType is null"); final Bundle optionsIn = new Bundle(); if (options != null) { @@ -2679,8 +2684,89 @@ public class AccountManager { return new AmsTask(activity, handler, callback) { @Override public void doWork() throws RemoteException { - mService.startAddAccountSession(mResponse, accountType, authTokenType, - requiredFeatures, activity != null, optionsIn); + mService.startAddAccountSession( + mResponse, + accountType, + authTokenType, + requiredFeatures, + activity != null, + optionsIn); + } + }.start(); + } + + /** + * Asks the user to enter a new password for an account but not updating the + * saved credentials for the account until 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 . + * + * @param account The account to update credentials for + * @param authTokenType The credentials entered must allow an auth token of + * this type to be created (but no actual auth token is + * returned); may be null + * @param options Authenticator-specific options for the request; may be + * null or empty + * @param activity The {@link Activity} context to use for launching a new + * authenticator-defined sub-Activity to prompt the user to enter + * a password; 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 user was + * successfully re-authenticated to the account (TODO: default impl + * only returns escorw?): + * <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 + * </ul> + * If no activity was specified, the returned Bundle contains + * {@link #KEY_INTENT} with the {@link Intent} needed to launch the + * password prompt. If an error occurred, + * {@link AccountManagerFuture#getResult()} throws: + * <ul> + * <li>{@link AuthenticatorException} if the authenticator failed to + * respond + * <li>{@link OperationCanceledException} if the operation was + * canceled for any reason, including the user canceling the + * password prompt + * <li>{@link IOException} if the authenticator experienced an I/O + * problem verifying the password, usually because of network + * trouble + * </ul> + */ + public AccountManagerFuture<Bundle> startUpdateCredentialsSession( + final Account account, + final String authTokenType, + final Bundle options, + final Activity activity, + final AccountManagerCallback<Bundle> callback, + final Handler handler) { + if (account == null) { + throw new IllegalArgumentException("account is null"); + } + return new AmsTask(activity, handler, callback) { + @Override + public void doWork() throws RemoteException { + mService.startUpdateCredentialsSession( + mResponse, + account, + authTokenType, + activity != null, + options); } }.start(); } diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl index b326070ddfc7..921fb19ddb63 100644 --- a/core/java/android/accounts/IAccountAuthenticator.aidl +++ b/core/java/android/accounts/IAccountAuthenticator.aidl @@ -90,4 +90,10 @@ oneway interface IAccountAuthenticator { */ void startAddAccountSession(in IAccountAuthenticatorResponse response, String accountType, String authTokenType, in String[] requiredFeatures, in Bundle options); + + /** + * Prompts the user for a new password but does not write it to the IAccountManager. + */ + void startUpdateCredentialsSession(in IAccountAuthenticatorResponse response, in Account account, + String authTokenType, in Bundle options); } diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl index 5de311ea6ec7..8489e47c0ba4 100644 --- a/core/java/android/accounts/IAccountManager.aidl +++ b/core/java/android/accounts/IAccountManager.aidl @@ -88,4 +88,7 @@ interface IAccountManager { String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch, in Bundle options); + /* Update credentials in two steps. */ + void startUpdateCredentialsSession(in IAccountManagerResponse response, in Account account, + String authTokenType, boolean expectActivityLaunch, in Bundle options); } diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 37d47c3b0568..93eaf0e66460 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -2286,8 +2286,11 @@ public class AccountManagerService } @Override - public void startAddAccountSession(final IAccountManagerResponse response, final String accountType, - final String authTokenType, final String[] requiredFeatures, + public void startAddAccountSession( + final IAccountManagerResponse response, + final String accountType, + final String authTokenType, + final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -2571,6 +2574,60 @@ public class AccountManagerService } @Override + public void startUpdateCredentialsSession( + IAccountManagerResponse response, + final Account account, + final String authTokenType, + final boolean expectActivityLaunch, + final Bundle loginOptions) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, + "startUpdateCredentialsSession: " + account + ", response " + response + + ", authTokenType " + authTokenType + ", expectActivityLaunch " + + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid() + + ", pid " + Binder.getCallingPid()); + } + if (response == null) { + throw new IllegalArgumentException("response is null"); + } + if (account == null) { + throw new IllegalArgumentException("account is null"); + } + int userId = UserHandle.getCallingUserId(); + long identityToken = clearCallingIdentity(); + try { + UserAccounts accounts = getUserAccounts(userId); + new StartAccountSession( + accounts, + response, + account.type, + expectActivityLaunch, + account.name, + false /* authDetailsRequired */, + true /* updateLastCredentialTime */) { + @Override + public void run() throws RemoteException { + mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType, + loginOptions); + } + + @Override + protected String toDebugString(long now) { + if (loginOptions != null) + loginOptions.keySet(); + return super.toDebugString(now) + + ", startUpdateCredentialsSession" + + ", " + account + + ", authTokenType " + authTokenType + + ", loginOptions " + loginOptions; + } + }.bind(); + } finally { + restoreCallingIdentity(identityToken); + } + } + + @Override public void editProperties(IAccountManagerResponse response, final String accountType, final boolean expectActivityLaunch) { final int callingUid = Binder.getCallingUid(); |