AccountManager: add startUpdateCredentials API.

Adding startUpdateCredentials API to AccountManager and
AbstractAccountAuthenticator.

Change-Id: Id9a1ff86764f2fde01fd8482594e4ae34e1f3bd1
This commit is contained in:
Sandra Kwan
2015-11-12 17:11:49 -08:00
parent 027bea555d
commit e68c37eead
7 changed files with 254 additions and 11 deletions

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}