summaryrefslogtreecommitdiffstats
path: root/core/java/android/accounts
diff options
context:
space:
mode:
authorFred Quintana <fredq@google.com>2009-04-20 16:05:10 -0700
committerFred Quintana <fredq@google.com>2009-04-20 16:06:02 -0700
commit3326920329cecb57c7ff1fc5c6add5c98aab9ed9 (patch)
tree13788c9643adef3c0c847d85443ee5696e8186dd /core/java/android/accounts
parente7c71d3a8cfb0c9c3637e0956fee3abc5a1fb094 (diff)
downloadframeworks_base-3326920329cecb57c7ff1fc5c6add5c98aab9ed9.zip
frameworks_base-3326920329cecb57c7ff1fc5c6add5c98aab9ed9.tar.gz
frameworks_base-3326920329cecb57c7ff1fc5c6add5c98aab9ed9.tar.bz2
adding concept of features to accounts
Diffstat (limited to 'core/java/android/accounts')
-rw-r--r--core/java/android/accounts/AbstractAccountAuthenticator.java28
-rw-r--r--core/java/android/accounts/AccountManager.java217
-rw-r--r--core/java/android/accounts/AccountManagerResponse.java74
-rw-r--r--core/java/android/accounts/AccountManagerService.java236
-rw-r--r--core/java/android/accounts/ChooseAccountActivity.java78
-rw-r--r--core/java/android/accounts/Constants.java2
-rw-r--r--core/java/android/accounts/IAccountAuthenticator.aidl9
-rw-r--r--core/java/android/accounts/IAccountManager.aidl8
-rw-r--r--core/java/android/accounts/IAccountManagerResponse.aidl1
9 files changed, 541 insertions, 112 deletions
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 587279a..474755c 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -27,13 +27,13 @@ import android.os.RemoteException;
public abstract class AbstractAccountAuthenticator {
class Transport extends IAccountAuthenticator.Stub {
public void addAccount(IAccountAuthenticatorResponse response, String accountType,
- String authTokenType, Bundle options)
+ String authTokenType, String[] requiredFeatures, Bundle options)
throws RemoteException {
final Bundle result;
try {
result = AbstractAccountAuthenticator.this.addAccount(
new AccountAuthenticatorResponse(response),
- accountType, authTokenType, options);
+ accountType, authTokenType, requiredFeatures, options);
} catch (NetworkErrorException e) {
response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
return;
@@ -133,6 +133,25 @@ public abstract class AbstractAccountAuthenticator {
response.onResult(result);
}
}
+
+ public void hasFeatures(IAccountAuthenticatorResponse response,
+ Account account, String[] features) throws RemoteException {
+ final Bundle result;
+ try {
+ result = AbstractAccountAuthenticator.this.hasFeatures(
+ new AccountAuthenticatorResponse(response), account, features);
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "hasFeatures not supported");
+ return;
+ } catch (NetworkErrorException e) {
+ response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+ return;
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
+ }
}
Transport mTransport = new Transport();
@@ -160,7 +179,8 @@ public abstract class AbstractAccountAuthenticator {
public abstract Bundle editProperties(AccountAuthenticatorResponse response,
String accountType);
public abstract Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
- String authTokenType, Bundle options) throws NetworkErrorException;
+ String authTokenType, String[] requiredFeatures, Bundle options)
+ throws NetworkErrorException;
/* @deprecated */
public abstract boolean confirmPassword(AccountAuthenticatorResponse response,
Account account, String password) throws NetworkErrorException;
@@ -171,4 +191,6 @@ public abstract class AbstractAccountAuthenticator {
throws NetworkErrorException;
public abstract Bundle updateCredentials(AccountAuthenticatorResponse response,
Account account, String authTokenType, Bundle loginOptions);
+ public abstract Bundle hasFeatures(AccountAuthenticatorResponse response,
+ Account account, String[] features) throws NetworkErrorException;
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index c60f15d..007a490 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -23,6 +23,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.Parcelable;
import java.io.IOException;
import java.util.concurrent.Callable;
@@ -33,7 +34,7 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
/**
- * A class that helps with interactions with the {@link IAccountManager} interface. It provides
+ * A class that helps with interactions with the AccountManagerService. It provides
* methods to allow for account, password, and authtoken management for all accounts on the
* device. Some of these calls are implemented with the help of the corresponding
* {@link IAccountAuthenticator} services. One accesses the {@link AccountManager} by calling:
@@ -48,6 +49,9 @@ public class AccountManager {
private final Context mContext;
private final IAccountManager mService;
+ /**
+ * @hide
+ */
public AccountManager(Context context, IAccountManager service) {
mContext = context;
mService = service;
@@ -337,7 +341,7 @@ public class AccountManager {
false /* notifyOnAuthFailure */, true /* expectActivityLaunch */,
loginOptions);
}
- };
+ }.start();
}
public Future2 getAuthToken(
@@ -350,18 +354,19 @@ public class AccountManager {
mService.getAuthToken(mResponse, account, authTokenType,
notifyAuthFailure, false /* expectActivityLaunch */, null /* options */);
}
- };
+ }.start();
}
public Future2 addAccount(final String accountType,
- final String authTokenType, final Bundle addAccountOptions,
+ final String authTokenType, final String[] requiredFeatures,
+ final Bundle addAccountOptions,
final Activity activity, Future2Callback callback, Handler handler) {
return new AmsTask(activity, handler, callback) {
public void doWork() throws RemoteException {
mService.addAcount(mResponse, accountType, authTokenType,
- activity != null, addAccountOptions);
+ requiredFeatures, activity != null, addAccountOptions);
}
- };
+ }.start();
}
/** @deprecated use {@link #confirmCredentials} instead */
@@ -374,6 +379,33 @@ public class AccountManager {
};
}
+ public Account[] blockingGetAccountsWithTypeAndFeatures(String type, String[] features)
+ throws AuthenticatorException, IOException, OperationCanceledException {
+ Future2 future = getAccountsWithTypeAndFeatures(type, features,
+ null /* callback */, null /* handler */);
+ Bundle result = future.getResult();
+ Parcelable[] accountsTemp = result.getParcelableArray(Constants.ACCOUNTS_KEY);
+ if (accountsTemp == null) {
+ throw new AuthenticatorException("accounts should not be null");
+ }
+ Account[] accounts = new Account[accountsTemp.length];
+ for (int i = 0; i < accountsTemp.length; i++) {
+ accounts[i] = (Account) accountsTemp[i];
+ }
+ return accounts;
+ }
+
+ public Future2 getAccountsWithTypeAndFeatures(
+ final String type, final String[] features,
+ Future2Callback callback, Handler handler) {
+ if (type == null) throw new IllegalArgumentException("type is null");
+ return new AmsTask(null /* activity */, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.getAccountsByTypeAndFeatures(mResponse, type, features);
+ }
+ }.start();
+ }
+
public Future2 confirmCredentials(final Account account, final Activity activity,
final Future2Callback callback,
final Handler handler) {
@@ -381,7 +413,7 @@ public class AccountManager {
public void doWork() throws RemoteException {
mService.confirmCredentials(mResponse, account, activity != null);
}
- };
+ }.start();
}
public Future2 updateCredentials(final Account account, final String authTokenType,
@@ -393,7 +425,7 @@ public class AccountManager {
mService.updateCredentials(mResponse, account, authTokenType, activity != null,
loginOptions);
}
- };
+ }.start();
}
public Future2 editProperties(final String accountType, final Activity activity,
@@ -403,7 +435,7 @@ public class AccountManager {
public void doWork() throws RemoteException {
mService.editProperties(mResponse, accountType, activity != null);
}
- };
+ }.start();
}
private void ensureNotOnMainThread() {
@@ -502,11 +534,12 @@ public class AccountManager {
}
}
- public abstract class AmsTask extends FutureTask<Bundle> implements Future2 {
+ private abstract class AmsTask extends FutureTask<Bundle> implements Future2 {
final IAccountManagerResponse mResponse;
final Handler mHandler;
final Future2Callback mCallback;
final Activity mActivity;
+ final Thread mThread;
public AmsTask(Activity activity, Handler handler, Future2Callback callback) {
super(new Callable<Bundle>() {
public Bundle call() throws Exception {
@@ -518,8 +551,7 @@ public class AccountManager {
mCallback = callback;
mActivity = activity;
mResponse = new Response();
-
- new Thread(new Runnable() {
+ mThread = new Thread(new Runnable() {
public void run() {
try {
doWork();
@@ -527,7 +559,12 @@ public class AccountManager {
// never happens
}
}
- }).start();
+ }, "AmsTask");
+ }
+
+ public final Future2 start() {
+ mThread.start();
+ return this;
}
public abstract void doWork() throws RemoteException;
@@ -609,7 +646,7 @@ public class AccountManager {
}
- public abstract class AMSTaskBoolean extends FutureTask<Boolean> implements Future1<Boolean> {
+ private abstract class AMSTaskBoolean extends FutureTask<Boolean> implements Future1<Boolean> {
final IAccountManagerResponse response;
final Handler mHandler;
final Future1Callback<Boolean> mCallback;
@@ -716,13 +753,159 @@ public class AccountManager {
}
if (code == Constants.ERROR_CODE_UNSUPPORTED_OPERATION) {
- return new UnsupportedOperationException();
+ return new UnsupportedOperationException(message);
}
if (code == Constants.ERROR_CODE_INVALID_RESPONSE) {
- return new AuthenticatorException("invalid response");
+ return new AuthenticatorException(message);
+ }
+
+ if (code == Constants.ERROR_CODE_BAD_ARGUMENTS) {
+ return new IllegalArgumentException(message);
+ }
+
+ return new AuthenticatorException(message);
+ }
+
+ private class GetAuthTokenByTypeAndFeaturesTask extends AmsTask implements Future2Callback {
+ GetAuthTokenByTypeAndFeaturesTask(final String accountType, final String authTokenType,
+ final String[] features, Activity activityForPrompting,
+ final Bundle addAccountOptions, final Bundle loginOptions,
+ Future2Callback callback, Handler handler) {
+ super(activityForPrompting, handler, callback);
+ if (accountType == null) throw new IllegalArgumentException("account type is null");
+ mAccountType = accountType;
+ mAuthTokenType = authTokenType;
+ mFeatures = features;
+ mAddAccountOptions = addAccountOptions;
+ mLoginOptions = loginOptions;
+ mMyCallback = this;
+ }
+ volatile Future2 mFuture = null;
+ final String mAccountType;
+ final String mAuthTokenType;
+ final String[] mFeatures;
+ final Bundle mAddAccountOptions;
+ final Bundle mLoginOptions;
+ final Future2Callback mMyCallback;
+
+ public void doWork() throws RemoteException {
+ getAccountsWithTypeAndFeatures(mAccountType, mFeatures, new Future2Callback() {
+ public void run(Future2 future) {
+ Bundle getAccountsResult;
+ try {
+ getAccountsResult = future.getResult();
+ } catch (OperationCanceledException e) {
+ setException(e);
+ return;
+ } catch (IOException e) {
+ setException(e);
+ return;
+ } catch (AuthenticatorException e) {
+ setException(e);
+ return;
+ }
+
+ Parcelable[] accounts =
+ getAccountsResult.getParcelableArray(Constants.ACCOUNTS_KEY);
+ if (accounts.length == 0) {
+ if (mActivity != null) {
+ // no accounts, add one now. pretend that the user directly
+ // made this request
+ mFuture = addAccount(mAccountType, mAuthTokenType, mFeatures,
+ mAddAccountOptions, mActivity, mMyCallback, mHandler);
+ } else {
+ // send result since we can't prompt to add an account
+ Bundle result = new Bundle();
+ result.putString(Constants.ACCOUNT_NAME_KEY, null);
+ result.putString(Constants.ACCOUNT_TYPE_KEY, null);
+ result.putString(Constants.AUTHTOKEN_KEY, null);
+ try {
+ mResponse.onResult(result);
+ } catch (RemoteException e) {
+ // this will never happen
+ }
+ // we are done
+ }
+ } else if (accounts.length == 1) {
+ // have a single account, return an authtoken for it
+ if (mActivity == null) {
+ mFuture = getAuthToken((Account) accounts[0], mAuthTokenType,
+ false /* notifyAuthFailure */, mMyCallback, mHandler);
+ } else {
+ mFuture = getAuthToken((Account) accounts[0],
+ mAuthTokenType, mLoginOptions,
+ mActivity, mMyCallback, mHandler);
+ }
+ } else {
+ if (mActivity != null) {
+ IAccountManagerResponse chooseResponse =
+ new IAccountManagerResponse.Stub() {
+ public void onResult(Bundle value) throws RemoteException {
+ Account account = new Account(
+ value.getString(Constants.ACCOUNT_NAME_KEY),
+ value.getString(Constants.ACCOUNT_TYPE_KEY));
+ mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions,
+ mActivity, mMyCallback, mHandler);
+ }
+
+ public void onError(int errorCode, String errorMessage)
+ throws RemoteException {
+ mResponse.onError(errorCode, errorMessage);
+ }
+ };
+ // have many accounts, launch the chooser
+ Intent intent = new Intent();
+ intent.setClassName("android",
+ "android.accounts.ChooseAccountActivity");
+ intent.putExtra(Constants.ACCOUNTS_KEY, accounts);
+ intent.putExtra(Constants.ACCOUNT_MANAGER_RESPONSE_KEY,
+ new AccountManagerResponse(chooseResponse));
+ mActivity.startActivity(intent);
+ // the result will arrive via the IAccountManagerResponse
+ } else {
+ // send result since we can't prompt to select an account
+ Bundle result = new Bundle();
+ result.putString(Constants.ACCOUNTS_KEY, null);
+ try {
+ mResponse.onResult(result);
+ } catch (RemoteException e) {
+ // this will never happen
+ }
+ // we are done
+ }
+ }
+ }}, mHandler);
+ }
+
+
+
+ // TODO(fredq) pass through the calls to our implemention of Future2 to the underlying
+ // future that we create. We need to do things like have cancel cancel the mFuture, if set
+ // or to cause this to be canceled if mFuture isn't set.
+ // Once this is done then getAuthTokenByFeatures can be changed to return a Future2.
+
+ public void run(Future2 future) {
+ try {
+ set(future.get());
+ } catch (InterruptedException e) {
+ cancel(true);
+ } catch (CancellationException e) {
+ cancel(true);
+ } catch (ExecutionException e) {
+ setException(e.getCause());
+ }
}
+ }
- return new AuthenticatorException("unknown error code");
+ public void getAuthTokenByFeatures(
+ final String accountType, final String authTokenType, final String[] features,
+ final Activity activityForPrompting, final Bundle addAccountOptions,
+ final Bundle loginOptions,
+ final Future2Callback callback, final Handler handler) {
+ if (accountType == null) throw new IllegalArgumentException("account type is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+ new GetAuthTokenByTypeAndFeaturesTask(accountType, authTokenType, features,
+ activityForPrompting, addAccountOptions, loginOptions, callback, handler).start();
}
}
diff --git a/core/java/android/accounts/AccountManagerResponse.java b/core/java/android/accounts/AccountManagerResponse.java
new file mode 100644
index 0000000..25371fd
--- /dev/null
+++ b/core/java/android/accounts/AccountManagerResponse.java
@@ -0,0 +1,74 @@
+/*
+ * 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.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+/**
+ * Object that wraps calls to an {@link android.accounts.IAccountManagerResponse} object.
+ * @hide
+ */
+public class AccountManagerResponse implements Parcelable {
+ private IAccountManagerResponse mResponse;
+
+ public AccountManagerResponse(IAccountManagerResponse response) {
+ mResponse = response;
+ }
+
+ public AccountManagerResponse(Parcel parcel) {
+ mResponse =
+ IAccountManagerResponse.Stub.asInterface(parcel.readStrongBinder());
+ }
+
+ public void onResult(Bundle result) {
+ try {
+ mResponse.onResult(result);
+ } catch (RemoteException e) {
+ // this should never happen
+ }
+ }
+
+ public void onError(int errorCode, String errorMessage) {
+ try {
+ mResponse.onError(errorCode, errorMessage);
+ } catch (RemoteException e) {
+ // this should never happen
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mResponse.asBinder());
+ }
+
+ public static final Creator<AccountManagerResponse> CREATOR =
+ new Creator<AccountManagerResponse>() {
+ public AccountManagerResponse createFromParcel(Parcel source) {
+ return new AccountManagerResponse(source);
+ }
+
+ public AccountManagerResponse[] newArray(int size) {
+ return new AccountManagerResponse[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 1f43620..ef0875c 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -59,6 +59,7 @@ import com.google.android.collect.Maps;
* instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
* AccountManager accountManager =
* (AccountManager)context.getSystemService(Context.ACCOUNT_SERVICE)
+ * @hide
*/
public class AccountManagerService extends IAccountManager.Stub {
private static final String TAG = "AccountManagerService";
@@ -79,7 +80,6 @@ public class AccountManagerService extends IAccountManager.Stub {
private final AccountAuthenticatorCache mAuthenticatorCache;
private final AuthenticatorBindHelper mBindHelper;
- public final HashMap<AuthTokenKey, String> mAuthTokenCache = Maps.newHashMap();
private final DatabaseHelper mOpenHelper;
private final SimWatcher mSimWatcher;
@@ -300,7 +300,7 @@ public class AccountManagerService extends IAccountManager.Stub {
}
}
db.setTransactionSuccessful();
- mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
+ sendAccountsChangedBroadcast();
return true;
} finally {
db.endTransaction();
@@ -321,22 +321,10 @@ public class AccountManagerService extends IAccountManager.Stub {
public void removeAccount(Account account) {
long identityToken = clearCallingIdentity();
try {
- synchronized (mAuthTokenCache) {
- ArrayList<AuthTokenKey> keysToRemove = Lists.newArrayList();
- for (AuthTokenKey key : mAuthTokenCache.keySet()) {
- if (key.mAccount.equals(account)) {
- keysToRemove.add(key);
- }
- }
- for (AuthTokenKey key : keysToRemove) {
- mAuthTokenCache.remove(key);
- }
-
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
- new String[]{account.mName, account.mType});
- mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
- }
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+ new String[]{account.mName, account.mType});
+ sendAccountsChangedBroadcast();
} finally {
restoreCallingIdentity(identityToken);
}
@@ -359,31 +347,26 @@ public class AccountManagerService extends IAccountManager.Stub {
}
private void invalidateAuthToken(SQLiteDatabase db, String accountType, String authToken) {
- synchronized (mAuthTokenCache) {
- Cursor cursor = db.rawQuery(
- "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
- + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
- + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
- + " FROM " + TABLE_ACCOUNTS
- + " JOIN " + TABLE_AUTHTOKENS
- + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
- + " = " + AUTHTOKENS_ACCOUNTS_ID
- + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
- + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
- new String[]{authToken, accountType});
- try {
- while (cursor.moveToNext()) {
- long authTokenId = cursor.getLong(0);
- String accountName = cursor.getString(1);
- String authTokenType = cursor.getString(2);
- AuthTokenKey key = new AuthTokenKey(new Account(accountName, accountType),
- authTokenType);
- mAuthTokenCache.remove(key);
- db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
- }
- } finally {
- cursor.close();
+ Cursor cursor = db.rawQuery(
+ "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
+ + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
+ + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
+ + " FROM " + TABLE_ACCOUNTS
+ + " JOIN " + TABLE_AUTHTOKENS
+ + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
+ + " = " + AUTHTOKENS_ACCOUNTS_ID
+ + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
+ + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
+ new String[]{authToken, accountType});
+ try {
+ while (cursor.moveToNext()) {
+ long authTokenId = cursor.getLong(0);
+ String accountName = cursor.getString(1);
+ String authTokenType = cursor.getString(2);
+ db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
}
+ } finally {
+ cursor.close();
}
}
@@ -391,8 +374,18 @@ public class AccountManagerService extends IAccountManager.Stub {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
try {
- if (saveAuthTokenToDatabase(db, account, type, authToken)) {
- mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
+ long accountId = getAccountId(db, account);
+ if (accountId < 0) {
+ return false;
+ }
+ db.delete(TABLE_AUTHTOKENS,
+ AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
+ new String[]{type});
+ ContentValues values = new ContentValues();
+ values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
+ values.put(AUTHTOKENS_TYPE, type);
+ values.put(AUTHTOKENS_AUTHTOKEN, authToken);
+ if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
db.setTransactionSuccessful();
return true;
}
@@ -402,22 +395,6 @@ public class AccountManagerService extends IAccountManager.Stub {
}
}
- private boolean saveAuthTokenToDatabase(SQLiteDatabase db, Account account,
- String type, String authToken) {
- long accountId = getAccountId(db, account);
- if (accountId < 0) {
- return false;
- }
- db.delete(TABLE_AUTHTOKENS,
- AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
- new String[]{type});
- ContentValues values = new ContentValues();
- values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
- values.put(AUTHTOKENS_TYPE, type);
- values.put(AUTHTOKENS_AUTHTOKEN, authToken);
- return db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0;
- }
-
public String readAuthTokenFromDatabase(Account account, String authTokenType) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
db.beginTransaction();
@@ -436,13 +413,7 @@ public class AccountManagerService extends IAccountManager.Stub {
public String peekAuthToken(Account account, String authTokenType) {
long identityToken = clearCallingIdentity();
try {
- synchronized (mAuthTokenCache) {
- AuthTokenKey key = new AuthTokenKey(account, authTokenType);
- if (mAuthTokenCache.containsKey(key)) {
- return mAuthTokenCache.get(key);
- }
- return readAuthTokenFromDatabase(account, authTokenType);
- }
+ return readAuthTokenFromDatabase(account, authTokenType);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -465,12 +436,16 @@ public class AccountManagerService extends IAccountManager.Stub {
mOpenHelper.getWritableDatabase().update(TABLE_ACCOUNTS, values,
ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
new String[]{account.mName, account.mType});
- mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
+ sendAccountsChangedBroadcast();
} finally {
restoreCallingIdentity(identityToken);
}
}
+ private void sendAccountsChangedBroadcast() {
+ mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
+ }
+
public void clearPassword(Account account) {
long identityToken = clearCallingIdentity();
try {
@@ -518,7 +493,7 @@ public class AccountManagerService extends IAccountManager.Stub {
final boolean expectActivityLaunch, final Bundle loginOptions) {
long identityToken = clearCallingIdentity();
try {
- String authToken = getCachedAuthToken(account, authTokenType);
+ String authToken = readAuthTokenFromDatabase(account, authTokenType);
if (authToken != null) {
try {
Bundle result = new Bundle();
@@ -578,19 +553,24 @@ public class AccountManagerService extends IAccountManager.Stub {
}
- public void addAcount(final IAccountManagerResponse response,
- final String accountType, final String authTokenType,
+ public void addAcount(final IAccountManagerResponse response, final String accountType,
+ final String authTokenType, final String[] requiredFeatures,
final boolean expectActivityLaunch, final Bundle options) {
long identityToken = clearCallingIdentity();
try {
new Session(response, accountType, expectActivityLaunch) {
public void run() throws RemoteException {
- mAuthenticator.addAccount(this, mAccountType, authTokenType, options);
+ mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
+ options);
}
protected String toDebugString(long now) {
return super.toDebugString(now) + ", addAccount"
- + ", accountType " + accountType;
+ + ", accountType " + accountType
+ + ", requiredFeatures "
+ + (requiredFeatures != null
+ ? TextUtils.join(",", requiredFeatures)
+ : null);
}
}.bind();
} finally {
@@ -674,24 +654,101 @@ public class AccountManagerService extends IAccountManager.Stub {
}
}
- private boolean cacheAuthToken(Account account, String authTokenType, String authToken) {
- synchronized (mAuthTokenCache) {
- if (saveAuthTokenToDatabase(account, authTokenType, authToken)) {
- final AuthTokenKey key = new AuthTokenKey(account, authTokenType);
- mAuthTokenCache.put(key, authToken);
- return true;
- } else {
- return false;
+ private class GetAccountsByTypeAndFeatureSession extends Session {
+ private final String[] mFeatures;
+ private volatile Account[] mAccountsOfType = null;
+ private volatile ArrayList<Account> mAccountsWithFeatures = null;
+ private volatile int mCurrentAccount = 0;
+
+ public GetAccountsByTypeAndFeatureSession(IAccountManagerResponse response,
+ String type, String[] features) {
+ super(response, type, false /* expectActivityLaunch */);
+ mFeatures = features;
+ }
+
+ public void run() throws RemoteException {
+ mAccountsOfType = getAccountsByType(mAccountType);
+ // check whether each account matches the requested features
+ mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
+ mCurrentAccount = 0;
+
+ checkAccount();
+ }
+
+ public void checkAccount() {
+ if (mCurrentAccount >= mAccountsOfType.length) {
+ sendResult();
+ return;
+ }
+
+ try {
+ mAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
+ } catch (RemoteException e) {
+ onError(Constants.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
}
}
- }
- private String getCachedAuthToken(Account account, String authTokenType) {
- synchronized (mAuthTokenCache) {
- final AuthTokenKey key = new AuthTokenKey(account, authTokenType);
- if (!mAuthTokenCache.containsKey(key)) return null;
- return mAuthTokenCache.get(key);
+ public void onResult(Bundle result) {
+ mNumResults++;
+ if (result == null) {
+ onError(Constants.ERROR_CODE_INVALID_RESPONSE, "null bundle");
+ return;
+ }
+ if (result.getBoolean(Constants.BOOLEAN_RESULT_KEY, false)) {
+ mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
+ }
+ mCurrentAccount++;
+ checkAccount();
}
+
+ public void sendResult() {
+ IAccountManagerResponse response = getResponseAndClose();
+ if (response != null) {
+ try {
+ Account[] accounts = new Account[mAccountsWithFeatures.size()];
+ for (int i = 0; i < accounts.length; i++) {
+ accounts[i] = mAccountsWithFeatures.get(i);
+ }
+ Bundle result = new Bundle();
+ result.putParcelableArray(Constants.ACCOUNTS_KEY, accounts);
+ response.onResult(result);
+ } catch (RemoteException e) {
+ // if the caller is dead then there is no one to care about remote exceptions
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "failure while notifying response", e);
+ }
+ }
+ }
+ }
+
+
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
+ + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
+ }
+ }
+ public void getAccountsByTypeAndFeatures(IAccountManagerResponse response,
+ String type, String[] features) {
+ if (type == null) {
+ if (response != null) {
+ try {
+ response.onError(Constants.ERROR_CODE_BAD_ARGUMENTS, "type is null");
+ } catch (RemoteException e) {
+ // ignore this
+ }
+ }
+ return;
+ }
+ long identityToken = clearCallingIdentity();
+ try {
+ new GetAccountsByTypeAndFeatureSession(response, type, features).bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private boolean cacheAuthToken(Account account, String authTokenType, String authToken) {
+ return saveAuthTokenToDatabase(account, authTokenType, authToken);
}
private long getAccountId(SQLiteDatabase db, Account account) {
@@ -743,7 +800,7 @@ public class AccountManagerService extends IAccountManager.Stub {
final boolean mExpectActivityLaunch;
final long mCreationTime;
- private int mNumResults = 0;
+ public int mNumResults = 0;
private int mNumRequestContinued = 0;
private int mNumErrors = 0;
@@ -754,6 +811,7 @@ public class AccountManagerService extends IAccountManager.Stub {
boolean expectActivityLaunch) {
super();
if (response == null) throw new IllegalArgumentException("response is null");
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
mResponse = response;
mAccountType = accountType;
mExpectActivityLaunch = expectActivityLaunch;
@@ -1071,7 +1129,7 @@ public class AccountManagerService extends IAccountManager.Stub {
try {
db.execSQL("DELETE from " + TABLE_AUTHTOKENS);
db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_PASSWORD + " = ''");
- mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
+ sendAccountsChangedBroadcast();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java
new file mode 100644
index 0000000..83377f3
--- /dev/null
+++ b/core/java/android/accounts/ChooseAccountActivity.java
@@ -0,0 +1,78 @@
+/*
+ * 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.ListActivity;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.view.View;
+import android.util.Log;
+
+public class ChooseAccountActivity extends ListActivity {
+ private static final String TAG = "AccountManager";
+ private Parcelable[] mAccounts = null;
+ private AccountManagerResponse mAccountManagerResponse = null;
+ private Bundle mResult;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ mAccounts = getIntent().getParcelableArrayExtra(Constants.ACCOUNTS_KEY);
+ mAccountManagerResponse =
+ getIntent().getParcelableExtra(Constants.ACCOUNT_MANAGER_RESPONSE_KEY);
+ } else {
+ mAccounts = savedInstanceState.getParcelableArray(Constants.ACCOUNTS_KEY);
+ mAccountManagerResponse =
+ savedInstanceState.getParcelable(Constants.ACCOUNT_MANAGER_RESPONSE_KEY);
+ }
+
+ String[] mAccountNames = new String[mAccounts.length];
+ for (int i = 0; i < mAccounts.length; i++) {
+ mAccountNames[i] = ((Account) mAccounts[i]).mName;
+ }
+
+ // Use an existing ListAdapter that will map an array
+ // of strings to TextViews
+ setListAdapter(new ArrayAdapter<String>(this,
+ android.R.layout.simple_list_item_1, mAccountNames));
+ getListView().setTextFilterEnabled(true);
+ }
+
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ Account account = (Account) mAccounts[position];
+ Log.d(TAG, "selected account " + account);
+ Bundle bundle = new Bundle();
+ bundle.putString(Constants.ACCOUNT_NAME_KEY, account.mName);
+ bundle.putString(Constants.ACCOUNT_TYPE_KEY, account.mType);
+ mResult = bundle;
+ finish();
+ }
+
+ public void finish() {
+ if (mAccountManagerResponse != null) {
+ if (mResult != null) {
+ mAccountManagerResponse.onResult(mResult);
+ } else {
+ mAccountManagerResponse.onError(Constants.ERROR_CODE_CANCELED, "canceled");
+ }
+ }
+ super.finish();
+ }
+}
diff --git a/core/java/android/accounts/Constants.java b/core/java/android/accounts/Constants.java
index d3b6aa0..b383c61 100644
--- a/core/java/android/accounts/Constants.java
+++ b/core/java/android/accounts/Constants.java
@@ -24,6 +24,7 @@ public class Constants {
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 String ACCOUNTS_KEY = "accounts";
public static final String AUTHENTICATOR_TYPES_KEY = "authenticator_types";
@@ -37,6 +38,7 @@ public class Constants {
public static final String INTENT_KEY = "intent";
public static final String BOOLEAN_RESULT_KEY = "booleanResult";
public static final String ACCOUNT_AUTHENTICATOR_RESPONSE_KEY = "accountAuthenticatorResponse";
+ public static final String ACCOUNT_MANAGER_RESPONSE_KEY = "accountManagerResponse";
public static final String AUTH_FAILED_MESSAGE_KEY = "authFailedMessage";
/**
* Action sent as a broadcast Intent by the AccountsService
diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl
index 70c0752..46a7144 100644
--- a/core/java/android/accounts/IAccountAuthenticator.aidl
+++ b/core/java/android/accounts/IAccountAuthenticator.aidl
@@ -28,7 +28,7 @@ oneway interface IAccountAuthenticator {
* prompts the user for account information and adds the result to the IAccountManager
*/
void addAccount(in IAccountAuthenticatorResponse response, String accountType,
- String authTokenType, in Bundle options);
+ String authTokenType, in String[] requiredFeatures, in Bundle options);
/**
* Checks that the account/password combination is valid.
@@ -58,4 +58,11 @@ oneway interface IAccountAuthenticator {
* launches an activity that lets the user edit and set the properties for an authenticator
*/
void editProperties(in IAccountAuthenticatorResponse response, String accountType);
+
+ /**
+ * returns a Bundle where the boolean value BOOLEAN_RESULT_KEY is set if the account has the
+ * specified features
+ */
+ void hasFeatures(in IAccountAuthenticatorResponse response, in Account account,
+ in String[] features);
}
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 365a92a..5e37a1f 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -22,6 +22,7 @@ import android.os.Bundle;
/**
* Central application service that provides account management.
+ * @hide
*/
interface IAccountManager {
String getPassword(in Account account);
@@ -42,16 +43,19 @@ interface IAccountManager {
String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch,
in Bundle options);
void addAcount(in IAccountManagerResponse response, String accountType,
- String authTokenType, boolean expectActivityLaunch, in Bundle options);
+ String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
+ in Bundle options);
void updateCredentials(in IAccountManagerResponse response, in Account account,
String authTokenType, boolean expectActivityLaunch, in Bundle options);
void editProperties(in IAccountManagerResponse response, String accountType,
boolean expectActivityLaunch);
void confirmCredentials(in IAccountManagerResponse response, in Account account,
boolean expectActivityLaunch);
+ void getAccountsByTypeAndFeatures(in IAccountManagerResponse response, String accountType,
+ in String[] features);
/*
- * @Deprecated
+ * @deprecated
*/
void confirmPassword(in IAccountManagerResponse response, in Account account,
String password);
diff --git a/core/java/android/accounts/IAccountManagerResponse.aidl b/core/java/android/accounts/IAccountManagerResponse.aidl
index 52f21bc..ca1203d 100644
--- a/core/java/android/accounts/IAccountManagerResponse.aidl
+++ b/core/java/android/accounts/IAccountManagerResponse.aidl
@@ -19,6 +19,7 @@ import android.os.Bundle;
/**
* The interface used to return responses for asynchronous calls to the {@link IAccountManager}
+ * @hide
*/
oneway interface IAccountManagerResponse {
void onResult(in Bundle value);