- add verbose logging to the authenticator
- fix a bug in AccountManager.getAuthTokenByFeatures() where getAuthToken()
isn't called after adding the account when necessary
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 8bc7428..c0c4c17 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -24,6 +24,10 @@
import android.content.Context;
import android.content.Intent;
import android.Manifest;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Arrays;
/**
* Abstract base class for creating AccountAuthenticators.
@@ -103,6 +107,8 @@
* writing activities to handle these requests.
*/
public abstract class AbstractAccountAuthenticator {
+ private static final String TAG = "AccountAuthenticator";
+
private final Context mContext;
public AbstractAccountAuthenticator(Context context) {
@@ -111,19 +117,34 @@
private class Transport extends IAccountAuthenticator.Stub {
public void addAccount(IAccountAuthenticatorResponse response, String accountType,
- String authTokenType, String[] requiredFeatures, Bundle options)
+ String authTokenType, String[] features, Bundle options)
throws RemoteException {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "addAccount: accountType " + accountType
+ + ", authTokenType " + authTokenType
+ + ", features " + (features == null ? "[]" : Arrays.toString(features)));
+ }
checkBinderPermission();
try {
final Bundle result = AbstractAccountAuthenticator.this.addAccount(
new AccountAuthenticatorResponse(response),
- accountType, authTokenType, requiredFeatures, options);
+ accountType, authTokenType, features, options);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ result.keySet(); // force it to be unparcelled
+ Log.v(TAG, "addAccount: result " + AccountManager.sanitizeResult(result));
+ }
if (result != null) {
response.onResult(result);
}
} catch (NetworkErrorException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "addAccount", e);
+ }
response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage());
} catch (UnsupportedOperationException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "addAccount", e);
+ }
response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
"addAccount not supported");
}
@@ -131,16 +152,30 @@
public void confirmCredentials(IAccountAuthenticatorResponse response,
Account account, Bundle options) throws RemoteException {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "confirmCredentials: " + account);
+ }
checkBinderPermission();
try {
final Bundle result = AbstractAccountAuthenticator.this.confirmCredentials(
new AccountAuthenticatorResponse(response), account, options);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ result.keySet(); // force it to be unparcelled
+ Log.v(TAG, "confirmCredentials: result "
+ + AccountManager.sanitizeResult(result));
+ }
if (result != null) {
response.onResult(result);
}
} catch (NetworkErrorException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "confirmCredentials", e);
+ }
response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage());
} catch (UnsupportedOperationException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "confirmCredentials", e);
+ }
response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
"confirmCredentials not supported");
}
@@ -149,16 +184,32 @@
public void getAuthTokenLabel(IAccountAuthenticatorResponse response,
String authTokenType)
throws RemoteException {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "getAuthTokenLabel: authTokenType " + authTokenType);
+ }
checkBinderPermission();
try {
Bundle result = new Bundle();
result.putString(AccountManager.KEY_AUTH_TOKEN_LABEL,
AbstractAccountAuthenticator.this.getAuthTokenLabel(authTokenType));
- response.onResult(result);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ result.keySet(); // force it to be unparcelled
+ Log.v(TAG, "getAuthTokenLabel: result "
+ + AccountManager.sanitizeResult(result));
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
} catch (IllegalArgumentException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "getAuthTokenLabel", e);
+ }
response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS,
"unknown authTokenType");
} catch (UnsupportedOperationException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "getAuthTokenLabel", e);
+ }
response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
"getAuthTokenTypeLabel not supported");
}
@@ -167,35 +218,64 @@
public void getAuthToken(IAccountAuthenticatorResponse response,
Account account, String authTokenType, Bundle loginOptions)
throws RemoteException {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "getAuthToken: " + account
+ + ", authTokenType " + authTokenType);
+ }
checkBinderPermission();
try {
final Bundle result = AbstractAccountAuthenticator.this.getAuthToken(
new AccountAuthenticatorResponse(response), account,
authTokenType, loginOptions);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ result.keySet(); // force it to be unparcelled
+ Log.v(TAG, "getAuthToken: result " + AccountManager.sanitizeResult(result));
+ }
if (result != null) {
response.onResult(result);
}
} catch (UnsupportedOperationException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "getAuthToken", e);
+ }
response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
"getAuthToken not supported");
} catch (NetworkErrorException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "getAuthToken", e);
+ }
response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage());
}
}
public void updateCredentials(IAccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle loginOptions) throws RemoteException {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "updateCredentials: " + account
+ + ", authTokenType " + authTokenType);
+ }
checkBinderPermission();
try {
final Bundle result = AbstractAccountAuthenticator.this.updateCredentials(
new AccountAuthenticatorResponse(response), account,
authTokenType, loginOptions);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ result.keySet(); // force it to be unparcelled
+ Log.v(TAG, "updateCredentials: result "
+ + AccountManager.sanitizeResult(result));
+ }
if (result != null) {
response.onResult(result);
}
} catch (NetworkErrorException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "updateCredentials", e);
+ }
response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage());
} catch (UnsupportedOperationException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "updateCredentials", e);
+ }
response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
"updateCredentials not supported");
}
diff --git a/core/java/android/accounts/AccountAuthenticatorResponse.java b/core/java/android/accounts/AccountAuthenticatorResponse.java
index 7c09fbf..614e139 100644
--- a/core/java/android/accounts/AccountAuthenticatorResponse.java
+++ b/core/java/android/accounts/AccountAuthenticatorResponse.java
@@ -20,11 +20,14 @@
import android.os.Parcelable;
import android.os.Parcel;
import android.os.RemoteException;
+import android.util.Log;
/**
* Object used to communicate responses back to the AccountManager
*/
public class AccountAuthenticatorResponse implements Parcelable {
+ private static final String TAG = "AccountAuthenticator";
+
private IAccountAuthenticatorResponse mAccountAuthenticatorResponse;
/**
@@ -40,6 +43,11 @@
}
public void onResult(Bundle result) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ result.keySet(); // force it to be unparcelled
+ Log.v(TAG, "AccountAuthenticatorResponse.onResult: "
+ + AccountManager.sanitizeResult(result));
+ }
try {
mAccountAuthenticatorResponse.onResult(result);
} catch (RemoteException e) {
@@ -48,6 +56,9 @@
}
public void onRequestContinued() {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "AccountAuthenticatorResponse.onRequestContinued");
+ }
try {
mAccountAuthenticatorResponse.onRequestContinued();
} catch (RemoteException e) {
@@ -56,6 +67,9 @@
}
public void onError(int errorCode, String errorMessage) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "AccountAuthenticatorResponse.onError: " + errorCode + ", " + errorMessage);
+ }
try {
mAccountAuthenticatorResponse.onError(errorCode, errorMessage);
} catch (RemoteException e) {
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 1bb1d0f..8356029 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -29,6 +29,7 @@
import android.os.Parcelable;
import android.os.Build;
import android.util.Log;
+import android.text.TextUtils;
import java.io.IOException;
import java.util.concurrent.Callable;
@@ -223,6 +224,19 @@
}
/**
+ * @hide for internal use only
+ */
+ public static Bundle sanitizeResult(Bundle result) {
+ if (result.containsKey(KEY_AUTHTOKEN)
+ && !TextUtils.isEmpty(result.getString(KEY_AUTHTOKEN))) {
+ final Bundle newResult = new Bundle(result);
+ newResult.putString(KEY_AUTHTOKEN, "<omitted for logging purposes>");
+ return newResult;
+ }
+ return result;
+ }
+
+ /**
* Gets an AccountManager instance associated with a Context.
* The {@link Context} will be used as long as the AccountManager is
* active, so make sure to use a {@link Context} whose lifetime is
@@ -1447,6 +1461,7 @@
final Bundle mAddAccountOptions;
final Bundle mLoginOptions;
final AccountManagerCallback<Bundle> mMyCallback;
+ private volatile int mNumAccounts = 0;
public void doWork() throws RemoteException {
getAccountsByTypeAndFeatures(mAccountType, mFeatures,
@@ -1466,6 +1481,8 @@
return;
}
+ mNumAccounts = accounts.length;
+
if (accounts.length == 0) {
if (mActivity != null) {
// no accounts, add one now. pretend that the user directly
@@ -1538,7 +1555,21 @@
public void run(AccountManagerFuture<Bundle> future) {
try {
- set(future.getResult());
+ final Bundle result = future.getResult();
+ if (mNumAccounts == 0) {
+ final String accountName = result.getString(KEY_ACCOUNT_NAME);
+ final String accountType = result.getString(KEY_ACCOUNT_TYPE);
+ if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
+ setException(new AuthenticatorException("account not in result"));
+ return;
+ }
+ final Account account = new Account(accountName, accountType);
+ mNumAccounts = 1;
+ getAuthToken(account, mAuthTokenType, null /* options */, mActivity,
+ mMyCallback, mHandler);
+ return;
+ }
+ set(result);
} catch (OperationCanceledException e) {
cancel(true /* mayInterruptIfRUnning */);
} catch (IOException e) {