@@ -60349,7 +61125,7 @@
type="float"
transient="false"
volatile="false"
- value="0.0010f"
+ value="0.001f"
static="true"
final="true"
deprecated="not deprecated"
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index ed683d7bb9dc9..587279a3696c8 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -16,6 +16,7 @@
package android.accounts;
+import android.os.Bundle;
import android.os.RemoteException;
/**
@@ -24,54 +25,113 @@ import android.os.RemoteException;
* AccountAuthenticators.
*/
public abstract class AbstractAccountAuthenticator {
- private static final String TAG = "AccountAuthenticator";
-
class Transport extends IAccountAuthenticator.Stub {
- public void addAccount(IAccountAuthenticatorResponse response, String accountType)
+ public void addAccount(IAccountAuthenticatorResponse response, String accountType,
+ String authTokenType, Bundle options)
throws RemoteException {
- AbstractAccountAuthenticator.this.addAccount(new AccountAuthenticatorResponse(response),
- accountType);
+ final Bundle result;
+ try {
+ result = AbstractAccountAuthenticator.this.addAccount(
+ new AccountAuthenticatorResponse(response),
+ accountType, authTokenType, options);
+ } catch (NetworkErrorException e) {
+ response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+ return;
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "addAccount not supported");
+ return;
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
}
- public void authenticateAccount(IAccountAuthenticatorResponse
- response, String name, String type, String password)
- throws RemoteException {
- AbstractAccountAuthenticator.this.authenticateAccount(
- new AccountAuthenticatorResponse(response), new Account(name, type), password);
+ public void confirmPassword(IAccountAuthenticatorResponse response,
+ Account account, String password) throws RemoteException {
+ boolean result;
+ try {
+ result = AbstractAccountAuthenticator.this.confirmPassword(
+ new AccountAuthenticatorResponse(response),
+ account, password);
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "confirmPassword not supported");
+ return;
+ } catch (NetworkErrorException e) {
+ response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+ return;
+ }
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(Constants.BOOLEAN_RESULT_KEY, result);
+ response.onResult(bundle);
+ }
+
+ public void confirmCredentials(IAccountAuthenticatorResponse response,
+ Account account) throws RemoteException {
+ final Bundle result;
+ try {
+ result = AbstractAccountAuthenticator.this.confirmCredentials(
+ new AccountAuthenticatorResponse(response), account);
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "confirmCredentials not supported");
+ return;
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
}
public void getAuthToken(IAccountAuthenticatorResponse response,
- String name, String type, String authTokenType)
+ Account account, String authTokenType, Bundle loginOptions)
throws RemoteException {
- AbstractAccountAuthenticator.this.getAuthToken(
- new AccountAuthenticatorResponse(response),
- new Account(name, type), authTokenType);
+ try {
+ final Bundle result = AbstractAccountAuthenticator.this.getAuthToken(
+ new AccountAuthenticatorResponse(response), account,
+ authTokenType, loginOptions);
+ if (result != null) {
+ response.onResult(result);
+ }
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "getAuthToken not supported");
+ } catch (NetworkErrorException e) {
+ response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+ }
}
- public void getPasswordStrength(IAccountAuthenticatorResponse response,
- String accountType, String password)
- throws RemoteException {
- AbstractAccountAuthenticator.this.getPasswordStrength(
- new AccountAuthenticatorResponse(response), accountType, password);
+ public void updateCredentials(IAccountAuthenticatorResponse response, Account account,
+ String authTokenType, Bundle loginOptions) throws RemoteException {
+ final Bundle result;
+ try {
+ result = AbstractAccountAuthenticator.this.updateCredentials(
+ new AccountAuthenticatorResponse(response), account,
+ authTokenType, loginOptions);
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "updateCredentials not supported");
+ return;
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
}
- public void checkUsernameExistence(IAccountAuthenticatorResponse response,
- String accountType, String username)
- throws RemoteException {
- AbstractAccountAuthenticator.this.checkUsernameExistence(
- new AccountAuthenticatorResponse(response), accountType, username);
- }
-
- public void updatePassword(IAccountAuthenticatorResponse response, String name, String type)
- throws RemoteException {
- AbstractAccountAuthenticator.this.updatePassword(
- new AccountAuthenticatorResponse(response), new Account(name, type));
- }
-
- public void editProperties(IAccountAuthenticatorResponse response, String accountType)
- throws RemoteException {
- AbstractAccountAuthenticator.this.editProperties(
+ public void editProperties(IAccountAuthenticatorResponse response,
+ String accountType) throws RemoteException {
+ final Bundle result;
+ try {
+ result = AbstractAccountAuthenticator.this.editProperties(
new AccountAuthenticatorResponse(response), accountType);
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "editProperties not supported");
+ return;
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
}
}
@@ -86,41 +146,29 @@ public abstract class AbstractAccountAuthenticator {
}
/**
- * prompts the user for account information and adds the result to the IAccountManager
+ * Returns a Bundle that contains the Intent of the activity that can be used to edit the
+ * properties. In order to indicate success the activity should call response.setResult()
+ * with a non-null Bundle.
+ * @param response used to set the result for the request. If the Constants.INTENT_KEY
+ * is set in the bundle then this response field is to be used for sending future
+ * results if and when the Intent is started.
+ * @param accountType the AccountType whose properties are to be edited.
+ * @return a Bundle containing the result or the Intent to start to continue the request.
+ * If this is null then the request is considered to still be active and the result should
+ * sent later using response.
*/
- public abstract void addAccount(AccountAuthenticatorResponse response, String accountType);
-
- /**
- * prompts the user for the credentials of the account
- */
- public abstract void authenticateAccount(AccountAuthenticatorResponse response,
- Account account, String password);
-
- /**
- * gets the password by either prompting the user or querying the IAccountManager
- */
- public abstract void getAuthToken(AccountAuthenticatorResponse response,
- Account account, String authTokenType);
-
- /**
- * does local analysis or uses a service in the cloud
- */
- public abstract void getPasswordStrength(AccountAuthenticatorResponse response,
- String accountType, String password);
-
- /**
- * checks with the login service in the cloud
- */
- public abstract void checkUsernameExistence(AccountAuthenticatorResponse response,
- String accountType, String username);
-
- /**
- * prompts the user for a new password and writes it to the IAccountManager
- */
- public abstract void updatePassword(AccountAuthenticatorResponse response, Account account);
-
- /**
- * launches an activity that lets the user edit and set the properties for an authenticator
- */
- public abstract void editProperties(AccountAuthenticatorResponse response, String accountType);
+ public abstract Bundle editProperties(AccountAuthenticatorResponse response,
+ String accountType);
+ public abstract Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
+ String authTokenType, Bundle options) throws NetworkErrorException;
+ /* @deprecated */
+ public abstract boolean confirmPassword(AccountAuthenticatorResponse response,
+ Account account, String password) throws NetworkErrorException;
+ public abstract Bundle confirmCredentials(AccountAuthenticatorResponse response,
+ Account account);
+ public abstract Bundle getAuthToken(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle loginOptions)
+ throws NetworkErrorException;
+ public abstract Bundle updateCredentials(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle loginOptions);
}
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index efcd3666af21b..30c91b0625340 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -18,6 +18,7 @@ package android.accounts;
import android.os.Parcelable;
import android.os.Parcel;
+import android.text.TextUtils;
/**
* Value type that represents an Account in the {@link AccountManager}. This object is
@@ -43,6 +44,12 @@ public class Account implements Parcelable {
}
public Account(String name, String type) {
+ if (TextUtils.isEmpty(name)) {
+ throw new IllegalArgumentException("the name must not be empty: " + name);
+ }
+ if (TextUtils.isEmpty(type)) {
+ throw new IllegalArgumentException("the type must not be empty: " + type);
+ }
mName = name;
mType = type;
}
diff --git a/core/java/android/accounts/AccountAuthenticatorActivity.java b/core/java/android/accounts/AccountAuthenticatorActivity.java
new file mode 100644
index 0000000000000..0319ab9bdc9e0
--- /dev/null
+++ b/core/java/android/accounts/AccountAuthenticatorActivity.java
@@ -0,0 +1,102 @@
+/*
+ * 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.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Base class for implementing an Activity that is used to help implement an
+ * AbstractAccountAuthenticator. If the AbstractAccountAuthenticator needs to return an Intent
+ * that is to be used to launch an Activity that needs to return results to satisfy an
+ * AbstractAccountAuthenticator request, it should store the AccountAuthenticatorResponse
+ * inside of the Intent as follows:
+ *
+ * intent.putExtra(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY, response);
+ *
+ * The activity that it launches should extend the AccountAuthenticatorActivity. If this
+ * activity has a result that satisfies the original request it sets it via:
+ *
+ * setAccountAuthenticatorResult(result)
+ *
+ * This result will be sent as the result of the request when the activity finishes. If this
+ * is never set or if it is set to null then the request will be canceled when the activity
+ * finishes.
+ */
+public class AccountAuthenticatorActivity extends Activity {
+ private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
+ private Bundle mResultBundle = null;
+
+ /**
+ * Set the result that is to be sent as the result of the request that caused this
+ * Activity to be launched. If result is null or this method is never called then
+ * the request will be canceled.
+ * @param result this is returned as the result of the AbstractAccountAuthenticator request
+ */
+ public final void setAccountAuthenticatorResult(Bundle result) {
+ mResultBundle = result;
+ }
+
+ /**
+ * Retreives the AccountAuthenticatorResponse from either the intent of the icicle, if the
+ * icicle is non-zero.
+ * @param icicle the save instance data of this Activity, may be null
+ */
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ if (icicle == null) {
+ Intent intent = getIntent();
+ mAccountAuthenticatorResponse =
+ intent.getParcelableExtra(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY);
+ } else {
+ mAccountAuthenticatorResponse =
+ icicle.getParcelable(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY);
+ }
+
+ if (mAccountAuthenticatorResponse != null) {
+ mAccountAuthenticatorResponse.onRequestContinued();
+ }
+ }
+
+ /**
+ * Saves the AccountAuthenticatorResponse in the instance state.
+ * @param outState where to store any instance data
+ */
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putParcelable(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY,
+ mAccountAuthenticatorResponse);
+ super.onSaveInstanceState(outState);
+ }
+
+ /**
+ * Sends the result or a Constants.ERROR_CODE_CANCELED error if a result isn't present.
+ */
+ public void finish() {
+ if (mAccountAuthenticatorResponse != null) {
+ // send the result bundle back if set, otherwise send an error.
+ if (mResultBundle != null) {
+ mAccountAuthenticatorResponse.onResult(mResultBundle);
+ } else {
+ mAccountAuthenticatorResponse.onError(Constants.ERROR_CODE_CANCELED, "canceled");
+ }
+ mAccountAuthenticatorResponse = null;
+ }
+ super.finish();
+ }
+}
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
index 72b6bdebcb75b..6a14ff80e5358 100644
--- a/core/java/android/accounts/AccountAuthenticatorCache.java
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -16,18 +16,27 @@
package android.accounts;
-import android.content.*;
-import android.content.res.XmlResourceParser;
-import android.content.res.TypedArray;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.XmlResourceParser;
+import android.content.res.TypedArray;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.util.Log;
import android.util.AttributeSet;
import android.util.Xml;
-import java.util.*;
import java.io.IOException;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
import com.google.android.collect.Maps;
import org.xmlpull.v1.XmlPullParserException;
@@ -59,6 +68,15 @@ public class AccountAuthenticatorCache {
};
}
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ getAllAuthenticators();
+ Map authenticators = mAuthenticators;
+ fout.println("AccountAuthenticatorCache: " + authenticators.size() + " authenticators");
+ for (AuthenticatorInfo info : authenticators.values()) {
+ fout.println(" " + info);
+ }
+ }
+
private void monitorPackageChanges() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -73,13 +91,15 @@ public class AccountAuthenticatorCache {
*/
public class AuthenticatorInfo {
public final String mType;
- public final String mComponentShortName;
public final ComponentName mComponentName;
private AuthenticatorInfo(String type, ComponentName componentName) {
mType = type;
mComponentName = componentName;
- mComponentShortName = componentName.flattenToShortString();
+ }
+
+ public String toString() {
+ return "AuthenticatorInfo: " + mType + ", " + mComponentName;
}
}
diff --git a/core/java/android/accounts/AccountAuthenticatorResponse.java b/core/java/android/accounts/AccountAuthenticatorResponse.java
index 07cee400b6437..7198046cfa254 100644
--- a/core/java/android/accounts/AccountAuthenticatorResponse.java
+++ b/core/java/android/accounts/AccountAuthenticatorResponse.java
@@ -16,38 +16,38 @@
package android.accounts;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.Parcel;
import android.os.RemoteException;
/**
* Object that wraps calls to an {@link IAccountAuthenticatorResponse} object.
* TODO: this interface is still in flux
*/
-public class AccountAuthenticatorResponse {
+public class AccountAuthenticatorResponse implements Parcelable {
private IAccountAuthenticatorResponse mAccountAuthenticatorResponse;
public AccountAuthenticatorResponse(IAccountAuthenticatorResponse response) {
mAccountAuthenticatorResponse = response;
}
- public void onFinished(int result) {
+ public AccountAuthenticatorResponse(Parcel parcel) {
+ mAccountAuthenticatorResponse =
+ IAccountAuthenticatorResponse.Stub.asInterface(parcel.readStrongBinder());
+ }
+
+ public void onResult(Bundle result) {
try {
- mAccountAuthenticatorResponse.onIntResult(result);
+ mAccountAuthenticatorResponse.onResult(result);
} catch (RemoteException e) {
// this should never happen
}
}
- public void onFinished(String result) {
+ public void onRequestContinued() {
try {
- mAccountAuthenticatorResponse.onStringResult(result);
- } catch (RemoteException e) {
- // this should never happen
- }
- }
-
- public void onFinished(boolean result) {
- try {
- mAccountAuthenticatorResponse.onBooleanResult(result);
+ mAccountAuthenticatorResponse.onRequestContinued();
} catch (RemoteException e) {
// this should never happen
}
@@ -61,7 +61,22 @@ public class AccountAuthenticatorResponse {
}
}
- public IAccountAuthenticatorResponse getIAccountAuthenticatorResponse() {
- return mAccountAuthenticatorResponse;
+ public int describeContents() {
+ return 0;
}
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mAccountAuthenticatorResponse.asBinder());
+ }
+
+ public static final Creator CREATOR =
+ new Creator() {
+ public AccountAuthenticatorResponse createFromParcel(Parcel source) {
+ return new AccountAuthenticatorResponse(source);
+ }
+
+ public AccountAuthenticatorResponse[] newArray(int size) {
+ return new AccountAuthenticatorResponse[size];
+ }
+ };
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index d0d475025c225..c60f15da11a5c 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -16,27 +16,31 @@
package android.accounts;
-import android.os.RemoteException;
-import android.os.Bundle;
-import android.app.PendingIntent;
import android.app.Activity;
import android.content.Intent;
import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.locks.Condition;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
/**
* A class that helps with interactions with the {@link IAccountManager} interface. 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:
- * AccountManager accountManager =
- * (AccountManager)context.getSystemService(Context.ACCOUNT_SERVICE)
+ * AccountManager accountManager = AccountManager.get(context);
*
*
- * TODO: this interface is still in flux
+ * TODO(fredq) this interface is still in flux
*/
public class AccountManager {
private static final String TAG = "AccountManager";
@@ -49,7 +53,12 @@ public class AccountManager {
mService = service;
}
- public String getPassword(Account account) {
+ public static AccountManager get(Context context) {
+ return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
+ }
+
+ public String blockingGetPassword(Account account) {
+ ensureNotOnMainThread();
try {
return mService.getPassword(account);
} catch (RemoteException e) {
@@ -58,7 +67,17 @@ public class AccountManager {
}
}
- public String getUserData(Account account, String key) {
+ public Future1 getPassword(final Future1Callback callback,
+ final Account account, final Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public String call() throws Exception {
+ return blockingGetPassword(account);
+ }
+ });
+ }
+
+ public String blockingGetUserData(Account account, String key) {
+ ensureNotOnMainThread();
try {
return mService.getUserData(account, key);
} catch (RemoteException e) {
@@ -67,7 +86,36 @@ public class AccountManager {
}
}
+ public Future1 getUserData(Future1Callback callback,
+ final Account account, final String key, Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public String call() throws Exception {
+ return blockingGetUserData(account, key);
+ }
+ });
+ }
+
+ public String[] blockingGetAuthenticatorTypes() {
+ ensureNotOnMainThread();
+ try {
+ return mService.getAuthenticatorTypes();
+ } catch (RemoteException e) {
+ // if this happens the entire runtime will restart
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Future1 getAuthenticatorTypes(Future1Callback callback,
+ Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public String[] call() throws Exception {
+ return blockingGetAuthenticatorTypes();
+ }
+ });
+ }
+
public Account[] blockingGetAccounts() {
+ ensureNotOnMainThread();
try {
return mService.getAccounts();
} catch (RemoteException e) {
@@ -76,34 +124,8 @@ public class AccountManager {
}
}
- public void getAccounts(final PendingIntent intent, final int code) {
- getAccountsByType(null /* all account types */, intent, code);
- }
-
- public void getAccountsByType(final String accountType,
- final PendingIntent intent, final int code) {
- Thread t = new Thread() {
- public void run() {
- try {
- Account[] accounts;
- if (accountType != null) {
- accounts = blockingGetAccountsByType(accountType);
- } else {
- accounts = blockingGetAccounts();
- }
- Intent payload = new Intent();
- payload.putExtra("accounts", accounts);
- intent.send(mContext, code, payload);
- } catch (PendingIntent.CanceledException e) {
- // the pending intent is no longer accepting results, we don't
- // need to do anything to handle this
- }
- }
- };
- t.start();
- }
-
public Account[] blockingGetAccountsByType(String accountType) {
+ ensureNotOnMainThread();
try {
return mService.getAccountsByType(accountType);
} catch (RemoteException e) {
@@ -112,7 +134,25 @@ public class AccountManager {
}
}
- public boolean addAccount(Account account, String password, Bundle extras) {
+ public Future1 getAccounts(Future1Callback callback, Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public Account[] call() throws Exception {
+ return blockingGetAccounts();
+ }
+ });
+ }
+
+ public Future1 getAccountsByType(Future1Callback callback,
+ final String type, Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public Account[] call() throws Exception {
+ return blockingGetAccountsByType(type);
+ }
+ });
+ }
+
+ public boolean blockingAddAccountExplicitly(Account account, String password, Bundle extras) {
+ ensureNotOnMainThread();
try {
return mService.addAccount(account, password, extras);
} catch (RemoteException e) {
@@ -121,7 +161,18 @@ public class AccountManager {
}
}
- public void removeAccount(Account account) {
+ public Future1 addAccountExplicitly(final Future1Callback callback,
+ final Account account, final String password, final Bundle extras,
+ final Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public Boolean call() throws Exception {
+ return blockingAddAccountExplicitly(account, password, extras);
+ }
+ });
+ }
+
+ public void blockingRemoveAccount(Account account) {
+ ensureNotOnMainThread();
try {
mService.removeAccount(account);
} catch (RemoteException e) {
@@ -129,7 +180,18 @@ public class AccountManager {
}
}
- public void invalidateAuthToken(String accountType, String authToken) {
+ public Future1 removeAccount(Future1Callback callback, final Account account,
+ final Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public Void call() throws Exception {
+ blockingRemoveAccount(account);
+ return null;
+ }
+ });
+ }
+
+ public void blockingInvalidateAuthToken(String accountType, String authToken) {
+ ensureNotOnMainThread();
try {
mService.invalidateAuthToken(accountType, authToken);
} catch (RemoteException e) {
@@ -137,7 +199,18 @@ public class AccountManager {
}
}
- public String peekAuthToken(Account account, String authTokenType) {
+ public Future1 invalidateAuthToken(Future1Callback callback,
+ final String accountType, final String authToken, final Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public Void call() throws Exception {
+ blockingInvalidateAuthToken(accountType, authToken);
+ return null;
+ }
+ });
+ }
+
+ public String blockingPeekAuthToken(Account account, String authTokenType) {
+ ensureNotOnMainThread();
try {
return mService.peekAuthToken(account, authTokenType);
} catch (RemoteException e) {
@@ -146,7 +219,17 @@ public class AccountManager {
}
}
- public void setPassword(Account account, String password) {
+ public Future1 peekAuthToken(Future1Callback callback,
+ final Account account, final String authTokenType, final Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public String call() throws Exception {
+ return blockingPeekAuthToken(account, authTokenType);
+ }
+ });
+ }
+
+ public void blockingSetPassword(Account account, String password) {
+ ensureNotOnMainThread();
try {
mService.setPassword(account, password);
} catch (RemoteException e) {
@@ -154,7 +237,18 @@ public class AccountManager {
}
}
- public void clearPassword(Account account) {
+ public Future1 setPassword(Future1Callback callback,
+ final Account account, final String password, final Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public Void call() throws Exception {
+ blockingSetPassword(account, password);
+ return null;
+ }
+ });
+ }
+
+ public void blockingClearPassword(Account account) {
+ ensureNotOnMainThread();
try {
mService.clearPassword(account);
} catch (RemoteException e) {
@@ -162,7 +256,18 @@ public class AccountManager {
}
}
- public void setUserData(Account account, String key, String value) {
+ public Future1 clearPassword(final Future1Callback callback, final Account account,
+ final Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public Void call() throws Exception {
+ blockingClearPassword(account);
+ return null;
+ }
+ });
+ }
+
+ public void blockingSetUserData(Account account, String key, String value) {
+ ensureNotOnMainThread();
try {
mService.setUserData(account, key, value);
} catch (RemoteException e) {
@@ -170,17 +275,18 @@ public class AccountManager {
}
}
- public void getAuthToken(AccountManagerResponse response,
- Account account, String authTokenType, boolean notifyAuthFailure) {
- try {
- mService.getAuthToken(response.getIAccountManagerResponse(), account, authTokenType,
- notifyAuthFailure);
- } catch (RemoteException e) {
- // if this happens the entire runtime will restart
- }
+ public Future1 setUserData(Future1Callback callback,
+ final Account account, final String key, final String value, final Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public Void call() throws Exception {
+ blockingSetUserData(account, key, value);
+ return null;
+ }
+ });
}
- public void setAuthToken(Account account, String authTokenType, String authToken) {
+ public void blockingSetAuthToken(Account account, String authTokenType, String authToken) {
+ ensureNotOnMainThread();
try {
mService.setAuthToken(account, authTokenType, authToken);
} catch (RemoteException e) {
@@ -188,119 +294,435 @@ public class AccountManager {
}
}
- public void addAccountInteractively(AccountManagerResponse response, String accountType) {
- try {
- mService.addAccountInteractively(response.getIAccountManagerResponse(), accountType);
- } catch (RemoteException e) {
- // if this happens the entire runtime will restart
+ public Future1 setAuthToken(Future1Callback callback,
+ final Account account, final String authTokenType, final String authToken,
+ final Handler handler) {
+ return startAsFuture(callback, handler, new Callable() {
+ public Void call() throws Exception {
+ blockingSetAuthToken(account, authTokenType, authToken);
+ return null;
+ }
+ });
+ }
+
+ public String blockingGetAuthToken(Account account, String authTokenType,
+ boolean notifyAuthFailure)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ ensureNotOnMainThread();
+ Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
+ null /* handler */).getResult();
+ return bundle.getString(Constants.AUTHTOKEN_KEY);
+ }
+
+ /**
+ * Request the auth token for this account/authTokenType. If this succeeds then the
+ * auth token will then be passed to the activity. If this results in an authentication
+ * failure then a login intent will be returned that can be invoked to prompt the user to
+ * update their credentials. This login activity will return the auth token to the calling
+ * activity. If activity is null then the login intent will not be invoked.
+ *
+ * @param account the account whose auth token should be retrieved
+ * @param authTokenType the auth token type that should be retrieved
+ * @param loginOptions
+ * @param activity the activity to launch the login intent, if necessary, and to which
+ */
+ public Future2 getAuthToken(
+ final Account account, final String authTokenType, final Bundle loginOptions,
+ final Activity activity, Future2Callback callback, Handler handler) {
+ if (activity == null) throw new IllegalArgumentException("activity is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.getAuthToken(mResponse, account, authTokenType,
+ false /* notifyOnAuthFailure */, true /* expectActivityLaunch */,
+ loginOptions);
+ }
+ };
+ }
+
+ public Future2 getAuthToken(
+ final Account account, final String authTokenType, final boolean notifyAuthFailure,
+ Future2Callback callback, Handler handler) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+ return new AmsTask(null, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.getAuthToken(mResponse, account, authTokenType,
+ notifyAuthFailure, false /* expectActivityLaunch */, null /* options */);
+ }
+ };
+ }
+
+ public Future2 addAccount(final String accountType,
+ final String authTokenType, 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);
+ }
+ };
+ }
+
+ /** @deprecated use {@link #confirmCredentials} instead */
+ public Future1 confirmPassword(final Account account, final String password,
+ Future1Callback callback, Handler handler) {
+ return new AMSTaskBoolean(handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.confirmPassword(response, account, password);
+ }
+ };
+ }
+
+ public Future2 confirmCredentials(final Account account, final Activity activity,
+ final Future2Callback callback,
+ final Handler handler) {
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.confirmCredentials(mResponse, account, activity != null);
+ }
+ };
+ }
+
+ public Future2 updateCredentials(final Account account, final String authTokenType,
+ final Bundle loginOptions, final Activity activity,
+ final Future2Callback callback,
+ final Handler handler) {
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.updateCredentials(mResponse, account, authTokenType, activity != null,
+ loginOptions);
+ }
+ };
+ }
+
+ public Future2 editProperties(final String accountType, final Activity activity,
+ final Future2Callback callback,
+ final Handler handler) {
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.editProperties(mResponse, accountType, activity != null);
+ }
+ };
+ }
+
+ private void ensureNotOnMainThread() {
+ final Looper looper = Looper.myLooper();
+ if (looper != null && looper == mContext.getMainLooper()) {
+ // We really want to throw an exception here, but GTalkService exercises this
+ // path quite a bit and needs some serious rewrite in order to work properly.
+ //noinspection ThrowableInstanceNeverThrow
+// Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs",
+// new Exception());
+ // TODO(fredq) remove the log and throw this exception when the callers are fixed
+// throw new IllegalStateException(
+// "calling this from your main thread can lead to deadlock");
}
}
- public class AuthenticateAccountThread extends Thread {
- public Lock mLock = new ReentrantLock();
- public Condition mCondition = mLock.newCondition();
- volatile boolean mSuccess = false;
- volatile boolean mFailure = false;
- volatile boolean mResult = false;
- private final Account mAccount;
- private final String mPassword;
- public AuthenticateAccountThread(Account account, String password) {
- mAccount = account;
- mPassword = password;
+ private void postToHandler(Handler handler, final Future2Callback callback,
+ final Future2 future) {
+ if (handler == null) {
+ handler = new Handler(mContext.getMainLooper());
}
- public void run() {
- try {
- IAccountManagerResponse response = new IAccountManagerResponse.Stub() {
- public void onStringResult(String value) throws RemoteException {
- }
+ final Handler innerHandler = handler;
+ innerHandler.post(new Runnable() {
+ public void run() {
+ callback.run(future);
+ }
+ });
+ }
- public void onIntResult(int value) throws RemoteException {
- }
+ private void postToHandler(Handler handler, final Future1Callback callback,
+ final Future1 future) {
+ if (handler == null) {
+ handler = new Handler(mContext.getMainLooper());
+ }
+ final Handler innerHandler = handler;
+ innerHandler.post(new Runnable() {
+ public void run() {
+ callback.run(future);
+ }
+ });
+ }
- public void onBooleanResult(boolean value) throws RemoteException {
- mLock.lock();
- try {
- if (!mFailure && !mSuccess) {
- mSuccess = true;
- mResult = value;
- mCondition.signalAll();
- }
- } finally {
- mLock.unlock();
- }
- }
+ private Future1 startAsFuture(Future1Callback callback, Handler handler,
+ Callable callable) {
+ final FutureTaskWithCallback task =
+ new FutureTaskWithCallback(callback, callable, handler);
+ new Thread(task).start();
+ return task;
+ }
- public void onError(int errorCode, String errorMessage) {
- mLock.lock();
- try {
- if (!mFailure && !mSuccess) {
- mFailure = true;
- mCondition.signalAll();
- }
- } finally {
- mLock.unlock();
- }
- }
- };
+ private class FutureTaskWithCallback extends FutureTask implements Future1 {
+ final Future1Callback mCallback;
+ final Handler mHandler;
- mService.authenticateAccount(response, mAccount, mPassword);
- } catch (RemoteException e) {
- // if this happens the entire runtime will restart
+ public FutureTaskWithCallback(Future1Callback callback, Callable callable,
+ Handler handler) {
+ super(callable);
+ mCallback = callback;
+ mHandler = handler;
+ }
+
+ protected void done() {
+ if (mCallback != null) {
+ postToHandler(mHandler, mCallback, this);
}
}
- }
- public boolean authenticateAccount(Account account, String password) {
- AuthenticateAccountThread thread = new AuthenticateAccountThread(account, password);
- thread.mLock.lock();
- thread.start();
- try {
- while (!thread.mSuccess && !thread.mFailure) {
- try {
- thread.mCondition.await();
- } catch (InterruptedException e) {
- // keep waiting
- }
- }
- return thread.mResult;
- } finally {
- thread.mLock.unlock();
+ public V internalGetResult(Long timeout, TimeUnit unit) throws OperationCanceledException {
+ try {
+ if (timeout == null) {
+ return get();
+ } else {
+ return get(timeout, unit);
+ }
+ } catch (InterruptedException e) {
+ // we will cancel the task below
+ } catch (CancellationException e) {
+ // we will cancel the task below
+ } catch (TimeoutException e) {
+ // we will cancel the task below
+ } catch (ExecutionException e) {
+ // this should never happen
+ throw new IllegalStateException(e.getCause());
+ } finally {
+ cancel(true /* interruptIfRunning */);
+ }
+ throw new OperationCanceledException();
+ }
+
+ public V getResult() throws OperationCanceledException {
+ return internalGetResult(null, null);
+ }
+
+ public V getResult(long timeout, TimeUnit unit) throws OperationCanceledException {
+ return internalGetResult(null, null);
}
}
- public void updatePassword(AccountManagerResponse response, Account account) {
- try {
- mService.updatePassword(response.getIAccountManagerResponse(), account);
- } catch (RemoteException e) {
- // if this happens the entire runtime will restart
+ public abstract class AmsTask extends FutureTask implements Future2 {
+ final IAccountManagerResponse mResponse;
+ final Handler mHandler;
+ final Future2Callback mCallback;
+ final Activity mActivity;
+ public AmsTask(Activity activity, Handler handler, Future2Callback callback) {
+ super(new Callable() {
+ public Bundle call() throws Exception {
+ throw new IllegalStateException("this should never be called");
+ }
+ });
+
+ mHandler = handler;
+ mCallback = callback;
+ mActivity = activity;
+ mResponse = new Response();
+
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ doWork();
+ } catch (RemoteException e) {
+ // never happens
+ }
+ }
+ }).start();
}
+
+ public abstract void doWork() throws RemoteException;
+
+ private Bundle internalGetResult(Long timeout, TimeUnit unit)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ try {
+ if (timeout == null) {
+ return get();
+ } else {
+ return get(timeout, unit);
+ }
+ } catch (CancellationException e) {
+ throw new OperationCanceledException();
+ } catch (TimeoutException e) {
+ // fall through and cancel
+ } catch (InterruptedException e) {
+ // fall through and cancel
+ } catch (ExecutionException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ } else if (cause instanceof UnsupportedOperationException) {
+ throw new AuthenticatorException(cause);
+ } else if (cause instanceof AuthenticatorException) {
+ throw (AuthenticatorException) cause;
+ } else if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ } else {
+ throw new IllegalStateException(cause);
+ }
+ } finally {
+ cancel(true /* interrupt if running */);
+ }
+ throw new OperationCanceledException();
+ }
+
+ public Bundle getResult()
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ return internalGetResult(null, null);
+ }
+
+ public Bundle getResult(long timeout, TimeUnit unit)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ return internalGetResult(timeout, unit);
+ }
+
+ protected void done() {
+ if (mCallback != null) {
+ postToHandler(mHandler, mCallback, this);
+ }
+ }
+
+ /** Handles the responses from the AccountManager */
+ private class Response extends IAccountManagerResponse.Stub {
+ public void onResult(Bundle bundle) {
+ Intent intent = bundle.getParcelable("intent");
+ if (intent != null && mActivity != null) {
+ // since the user provided an Activity we will silently start intents
+ // that we see
+ mActivity.startActivity(intent);
+ // leave the Future running to wait for the real response to this request
+ } else {
+ set(bundle);
+ }
+ }
+
+ public void onError(int code, String message) {
+ if (code == Constants.ERROR_CODE_CANCELED) {
+ // the authenticator indicated that this request was canceled, do so now
+ cancel(true /* mayInterruptIfRunning */);
+ return;
+ }
+ setException(convertErrorToException(code, message));
+ }
+ }
+
}
- public void editProperties(AccountManagerResponse response, String accountType) {
- try {
- mService.editProperties(response.getIAccountManagerResponse(), accountType);
- } catch (RemoteException e) {
- // if this happens the entire runtime will restart
+ public abstract class AMSTaskBoolean extends FutureTask implements Future1 {
+ final IAccountManagerResponse response;
+ final Handler mHandler;
+ final Future1Callback mCallback;
+ public AMSTaskBoolean(Handler handler, Future1Callback callback) {
+ super(new Callable() {
+ public Boolean call() throws Exception {
+ throw new IllegalStateException("this should never be called");
+ }
+ });
+
+ mHandler = handler;
+ mCallback = callback;
+ response = new Response();
+
+ new Thread(new Runnable() {
+ public void run() {
+ try {
+ doWork();
+ } catch (RemoteException e) {
+ // never happens
+ }
+ }
+ }).start();
}
+
+ public abstract void doWork() throws RemoteException;
+
+
+ protected void done() {
+ if (mCallback != null) {
+ postToHandler(mHandler, mCallback, this);
+ }
+ }
+
+ private Boolean internalGetResult(Long timeout, TimeUnit unit) {
+ try {
+ if (timeout == null) {
+ return get();
+ } else {
+ return get(timeout, unit);
+ }
+ } catch (InterruptedException e) {
+ // fall through and cancel
+ } catch (TimeoutException e) {
+ // fall through and cancel
+ } catch (CancellationException e) {
+ return false;
+ } catch (ExecutionException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ return false;
+ } else if (cause instanceof UnsupportedOperationException) {
+ return false;
+ } else if (cause instanceof AuthenticatorException) {
+ return false;
+ } else if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ } else {
+ throw new IllegalStateException(cause);
+ }
+ } finally {
+ cancel(true /* interrupt if running */);
+ }
+ return false;
+ }
+
+ public Boolean getResult() throws OperationCanceledException {
+ return internalGetResult(null, null);
+ }
+
+ public Boolean getResult(long timeout, TimeUnit unit) throws OperationCanceledException {
+ return internalGetResult(timeout, unit);
+ }
+
+ private class Response extends IAccountManagerResponse.Stub {
+ public void onResult(Bundle bundle) {
+ try {
+ if (bundle.containsKey(Constants.BOOLEAN_RESULT_KEY)) {
+ set(bundle.getBoolean(Constants.BOOLEAN_RESULT_KEY));
+ return;
+ }
+ } catch (ClassCastException e) {
+ // we will set the exception below
+ }
+ onError(Constants.ERROR_CODE_INVALID_RESPONSE, "no result in response");
+ }
+
+ public void onError(int code, String message) {
+ if (code == Constants.ERROR_CODE_CANCELED) {
+ cancel(true /* mayInterruptIfRunning */);
+ return;
+ }
+ setException(convertErrorToException(code, message));
+ }
+ }
+
}
- public void getPasswordStrength(AccountManagerResponse response,
- String accountType, String password) {
- try {
- mService.getPasswordStrength(response.getIAccountManagerResponse(),
- accountType, password);
- } catch (RemoteException e) {
- // if this happens the entire runtime will restart
+ private Exception convertErrorToException(int code, String message) {
+ if (code == Constants.ERROR_CODE_NETWORK_ERROR) {
+ return new IOException(message);
}
- }
- public void checkUsernameExistence(AccountManagerResponse response,
- String accountType, String username) {
- try {
- mService.checkUsernameExistence(response.getIAccountManagerResponse(),
- accountType, username);
- } catch (RemoteException e) {
- // if this happens the entire runtime will restart
+ if (code == Constants.ERROR_CODE_UNSUPPORTED_OPERATION) {
+ return new UnsupportedOperationException();
}
+
+ if (code == Constants.ERROR_CODE_INVALID_RESPONSE) {
+ return new AuthenticatorException("invalid response");
+ }
+
+ return new AuthenticatorException("unknown error code");
}
}
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 78d4535f6bbac..29074ccbfeb43 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -16,19 +16,37 @@
package android.accounts;
-import android.os.*;
-import android.content.*;
-import android.database.sqlite.*;
+import android.content.BroadcastReceiver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.database.Cursor;
import android.database.DatabaseUtils;
-import android.util.Log;
-import android.text.TextUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedHashMap;
-import com.google.android.collect.Maps;
import com.android.internal.telephony.TelephonyIntents;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
/**
* A system service that provides account, password, and authtoken management for all
@@ -43,7 +61,7 @@ public class AccountManagerService extends IAccountManager.Stub {
private static final int TIMEOUT_DELAY_MS = 1000 * 60;
private static final String DATABASE_NAME = "accounts.db";
- private static final int DATABASE_VERSION = 1;
+ private static final int DATABASE_VERSION = 2;
private final Context mContext;
@@ -86,7 +104,10 @@ public class AccountManagerService extends IAccountManager.Stub {
private static final String[] ACCOUNT_NAME_TYPE_PROJECTION =
new String[]{ACCOUNTS_ID, ACCOUNTS_NAME, ACCOUNTS_TYPE};
private static final Intent ACCOUNTS_CHANGED_INTENT =
- new Intent(AccountsServiceConstants.LOGIN_ACCOUNTS_CHANGED_ACTION);
+ new Intent(Constants.LOGIN_ACCOUNTS_CHANGED_ACTION);
+
+ private final LinkedHashMap mSessions = new LinkedHashMap();
+ private static final int NOTIFICATION_ID = 234;
public class AuthTokenKey {
public final Account mAccount;
@@ -143,7 +164,7 @@ public class AccountManagerService extends IAccountManager.Stub {
mSimWatcher = new SimWatcher(mContext);
}
- public String getPassword(Account account) throws RemoteException {
+ public String getPassword(Account account) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
@@ -158,7 +179,7 @@ public class AccountManagerService extends IAccountManager.Stub {
}
}
- public String getUserData(Account account, String key) throws RemoteException {
+ public String getUserData(Account account, String key) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
db.beginTransaction();
try {
@@ -183,11 +204,23 @@ public class AccountManagerService extends IAccountManager.Stub {
}
}
- public Account[] getAccounts() throws RemoteException {
+ public String[] getAuthenticatorTypes() {
+ Collection authenticatorCollection =
+ mAuthenticatorCache.getAllAuthenticators();
+ String[] types = new String[authenticatorCollection.size()];
+ int i = 0;
+ for (AccountAuthenticatorCache.AuthenticatorInfo authenticator : authenticatorCollection) {
+ types[i] = authenticator.mType;
+ i++;
+ }
+ return types;
+ }
+
+ public Account[] getAccounts() {
return getAccountsByType(null);
}
- public Account[] getAccountsByType(String accountType) throws RemoteException {
+ public Account[] getAccountsByType(String accountType) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
final String selection = accountType == null ? null : (ACCOUNTS_TYPE + "=?");
@@ -207,8 +240,7 @@ public class AccountManagerService extends IAccountManager.Stub {
}
}
- public boolean addAccount(Account account, String password, Bundle extras)
- throws RemoteException {
+ public boolean addAccount(Account account, String password, Bundle extras) {
// fails if the account already exists
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
@@ -252,15 +284,26 @@ public class AccountManagerService extends IAccountManager.Stub {
return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
}
- public void removeAccount(Account account) throws RemoteException {
- // clear out matching authtokens from the cache
- 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);
+ public void removeAccount(Account account) {
+ synchronized (mAuthTokenCache) {
+ ArrayList 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);
+ }
}
- public void invalidateAuthToken(String accountType, String authToken) throws RemoteException {
+ public void invalidateAuthToken(String accountType, String authToken) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
try {
@@ -272,29 +315,31 @@ public class AccountManagerService extends IAccountManager.Stub {
}
private void invalidateAuthToken(SQLiteDatabase db, String accountType, String authToken) {
- 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);
+ 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();
}
- } finally {
- cursor.close();
}
}
@@ -344,20 +389,21 @@ public class AccountManagerService extends IAccountManager.Stub {
}
}
- public String peekAuthToken(Account account, String authTokenType) throws RemoteException {
- AuthTokenKey key = new AuthTokenKey(account, authTokenType);
- if (mAuthTokenCache.containsKey(key)) {
- return mAuthTokenCache.get(key);
+ public String peekAuthToken(Account account, String authTokenType) {
+ synchronized (mAuthTokenCache) {
+ AuthTokenKey key = new AuthTokenKey(account, authTokenType);
+ if (mAuthTokenCache.containsKey(key)) {
+ return mAuthTokenCache.get(key);
+ }
+ return readAuthTokenFromDatabase(account, authTokenType);
}
- return readAuthTokenFromDatabase(account, authTokenType);
}
- public void setAuthToken(Account account, String authTokenType, String authToken)
- throws RemoteException {
+ public void setAuthToken(Account account, String authTokenType, String authToken) {
cacheAuthToken(account, authTokenType, authToken);
}
- public void setPassword(Account account, String password) throws RemoteException {
+ public void setPassword(Account account, String password) {
ContentValues values = new ContentValues();
values.put(ACCOUNTS_PASSWORD, password);
mOpenHelper.getWritableDatabase().update(TABLE_ACCOUNTS, values,
@@ -366,11 +412,11 @@ public class AccountManagerService extends IAccountManager.Stub {
mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
}
- public void clearPassword(Account account) throws RemoteException {
+ public void clearPassword(Account account) {
setPassword(account, null);
}
- public void setUserData(Account account, String key, String value) throws RemoteException {
+ public void setUserData(Account account, String key, String value) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.beginTransaction();
try {
@@ -398,66 +444,154 @@ public class AccountManagerService extends IAccountManager.Stub {
}
}
- public void getAuthToken(IAccountManagerResponse response, Account account,
- String authTokenType, boolean notifyOnAuthFailure) throws RemoteException {
- // create a new Session
- Session session = new GetAuthTokenSession(response, account, authTokenType,
- notifyOnAuthFailure);
-
+ public void getAuthToken(IAccountManagerResponse response, final Account account,
+ final String authTokenType, final boolean notifyOnAuthFailure,
+ final boolean expectActivityLaunch, final Bundle loginOptions) {
String authToken = getCachedAuthToken(account, authTokenType);
if (authToken != null) {
- session.onStringResult(authToken);
+ try {
+ Bundle result = new Bundle();
+ result.putString(Constants.AUTHTOKEN_KEY, authToken);
+ result.putString(Constants.ACCOUNT_NAME_KEY, account.mName);
+ result.putString(Constants.ACCOUNT_TYPE_KEY, account.mType);
+ 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);
+ }
+ }
return;
}
- session.bind();
+ new Session(response, account.mType, expectActivityLaunch) {
+ protected String toDebugString(long now) {
+ if (loginOptions != null) loginOptions.keySet();
+ return super.toDebugString(now) + ", getAuthToken"
+ + ", " + account
+ + ", authTokenType " + authTokenType
+ + ", loginOptions " + loginOptions
+ + ", notifyOnAuthFailure " + notifyOnAuthFailure;
+ }
+
+ public void run() throws RemoteException {
+ mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
+ }
+
+ public void onResult(Bundle result) {
+ if (result != null) {
+ String authToken = result.getString(Constants.AUTHTOKEN_KEY);
+ if (authToken != null) {
+ String name = result.getString(Constants.ACCOUNT_NAME_KEY);
+ String type = result.getString(Constants.ACCOUNT_TYPE_KEY);
+ if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
+ onError(Constants.ERROR_CODE_INVALID_RESPONSE,
+ "the type and name should not be empty");
+ return;
+ }
+ cacheAuthToken(new Account(name, type), authTokenType, authToken);
+ }
+
+ Intent intent = result.getParcelable(Constants.INTENT_KEY);
+ if (intent != null && notifyOnAuthFailure) {
+ doNotification(result.getString(Constants.AUTH_FAILED_MESSAGE_KEY), intent);
+ }
+ }
+ super.onResult(result);
+ }
+ }.bind();
}
- public void addAccountInteractively(IAccountManagerResponse response, String accountType)
- throws RemoteException {
- new AddAccountInteractivelySession(response, accountType).bind();
+
+ public void addAcount(final IAccountManagerResponse response,
+ final String accountType, final String authTokenType,
+ final boolean expectActivityLaunch, final Bundle options) {
+ new Session(response, accountType, expectActivityLaunch) {
+ public void run() throws RemoteException {
+ mAuthenticator.addAccount(this, mAccountType, authTokenType, options);
+ }
+
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", addAccount"
+ + ", accountType " + accountType;
+ }
+ }.bind();
}
- public void authenticateAccount(IAccountManagerResponse response, Account account,
- String password)
- throws RemoteException {
- new AuthenticateAccountSession(response, account, password).bind();
+ public void confirmCredentials(IAccountManagerResponse response,
+ final Account account, final boolean expectActivityLaunch) {
+ new Session(response, account.mType, expectActivityLaunch) {
+ public void run() throws RemoteException {
+ mAuthenticator.confirmCredentials(this, account);
+ }
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", confirmCredentials"
+ + ", " + account;
+ }
+ }.bind();
}
- public void updatePassword(IAccountManagerResponse response, Account account)
- throws RemoteException {
- new UpdatePasswordSession(response, account).bind();
+ public void confirmPassword(IAccountManagerResponse response, final Account account,
+ final String password) {
+ new Session(response, account.mType, false /* expectActivityLaunch */) {
+ public void run() throws RemoteException {
+ mAuthenticator.confirmPassword(this, account, password);
+ }
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", confirmPassword"
+ + ", " + account;
+ }
+ }.bind();
}
- public void editProperties(IAccountManagerResponse response, String accountType)
- throws RemoteException {
- new EditPropertiesSession(response, accountType).bind();
+ public void updateCredentials(IAccountManagerResponse response, final Account account,
+ final String authTokenType, final boolean expectActivityLaunch,
+ final Bundle loginOptions) {
+ new Session(response, account.mType, expectActivityLaunch) {
+ public void run() throws RemoteException {
+ mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
+ }
+ protected String toDebugString(long now) {
+ if (loginOptions != null) loginOptions.keySet();
+ return super.toDebugString(now) + ", updateCredentials"
+ + ", " + account
+ + ", authTokenType " + authTokenType
+ + ", loginOptions " + loginOptions;
+ }
+ }.bind();
}
- public void getPasswordStrength(IAccountManagerResponse response,
- String accountType, String password) throws RemoteException {
- new GetPasswordStrengthSession(response, accountType, password).bind();
- }
-
- public void checkUsernameExistence(IAccountManagerResponse response,
- String accountType, String username) throws RemoteException {
- new CheckUsernameExistenceSession(response, username, accountType).bind();
+ public void editProperties(IAccountManagerResponse response, final String accountType,
+ final boolean expectActivityLaunch) {
+ new Session(response, accountType, expectActivityLaunch) {
+ public void run() throws RemoteException {
+ mAuthenticator.editProperties(this, mAccountType);
+ }
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", editProperties"
+ + ", accountType " + accountType;
+ }
+ }.bind();
}
private boolean cacheAuthToken(Account account, String authTokenType, String authToken) {
- if (saveAuthTokenToDatabase(account, authTokenType, authToken)) {
- final AuthTokenKey key = new AuthTokenKey(account, authTokenType);
- mAuthTokenCache.put(key, authToken);
- return true;
- } else {
- return false;
+ synchronized (mAuthTokenCache) {
+ if (saveAuthTokenToDatabase(account, authTokenType, authToken)) {
+ final AuthTokenKey key = new AuthTokenKey(account, authTokenType);
+ mAuthTokenCache.put(key, authToken);
+ return true;
+ } else {
+ return false;
+ }
}
}
private String getCachedAuthToken(Account account, String authTokenType) {
- final AuthTokenKey key = new AuthTokenKey(account, authTokenType);
- if (!mAuthTokenCache.containsKey(key)) return null;
- return mAuthTokenCache.get(key);
+ synchronized (mAuthTokenCache) {
+ final AuthTokenKey key = new AuthTokenKey(account, authTokenType);
+ if (!mAuthTokenCache.containsKey(key)) return null;
+ return mAuthTokenCache.get(key);
+ }
}
private long getAccountId(SQLiteDatabase db, Account account) {
@@ -502,34 +636,91 @@ public class AccountManagerService extends IAccountManager.Stub {
}
}
- private class Session extends IAccountAuthenticatorResponse.Stub
- implements AuthenticatorBindHelper.Callback {
+ private abstract class Session extends IAccountAuthenticatorResponse.Stub
+ implements AuthenticatorBindHelper.Callback, IBinder.DeathRecipient {
IAccountManagerResponse mResponse;
final String mAccountType;
+ final boolean mExpectActivityLaunch;
+ final long mCreationTime;
+
+ private int mNumResults = 0;
+ private int mNumRequestContinued = 0;
+ private int mNumErrors = 0;
+
IAccountAuthenticator mAuthenticator = null;
- public Session(IAccountManagerResponse response, String accountType) {
+ public Session(IAccountManagerResponse response, String accountType,
+ boolean expectActivityLaunch) {
super();
+ if (response == null) throw new IllegalArgumentException("response is null");
mResponse = response;
mAccountType = accountType;
+ mExpectActivityLaunch = expectActivityLaunch;
+ mCreationTime = SystemClock.elapsedRealtime();
+ synchronized (mSessions) {
+ mSessions.put(toString(), this);
+ }
+ try {
+ response.asBinder().linkToDeath(this, 0 /* flags */);
+ } catch (RemoteException e) {
+ mResponse = null;
+ binderDied();
+ }
}
- IAccountManagerResponse close() {
+ IAccountManagerResponse getResponseAndClose() {
if (mResponse == null) {
// this session has already been closed
return null;
}
- cancelTimeout();
- unbind();
IAccountManagerResponse response = mResponse;
- mResponse = null;
+ close(); // this clears mResponse so we need to save the response before this call
return response;
}
+ private void close() {
+ synchronized (mSessions) {
+ if (mSessions.remove(toString()) == null) {
+ // the session was already closed, so bail out now
+ return;
+ }
+ }
+ if (mResponse != null) {
+ // stop listening for response deaths
+ mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
+
+ // clear this so that we don't accidentally send any further results
+ mResponse = null;
+ }
+ cancelTimeout();
+ unbind();
+ }
+
+ public void binderDied() {
+ mResponse = null;
+ close();
+ }
+
+ protected String toDebugString() {
+ return toDebugString(SystemClock.elapsedRealtime());
+ }
+
+ protected String toDebugString(long now) {
+ return "Session: expectLaunch " + mExpectActivityLaunch
+ + ", connected " + (mAuthenticator != null)
+ + ", stats (" + mNumResults + "/" + mNumRequestContinued
+ + "/" + mNumErrors + ")"
+ + ", lifetime " + ((now - mCreationTime) / 1000.0);
+ }
+
void bind() {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
+ }
if (!mBindHelper.bind(mAccountType, this)) {
- onError(6, "bind failure");
+ Log.d(TAG, "bind attempt failed for " + toDebugString());
+ onError(Constants.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
}
}
@@ -551,206 +742,87 @@ public class AccountManagerService extends IAccountManager.Stub {
public void onConnected(IBinder service) {
mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
- // do the next step
+ try {
+ run();
+ } catch (RemoteException e) {
+ onError(Constants.ERROR_CODE_REMOTE_EXCEPTION,
+ "remote exception");
+ }
}
+ public abstract void run() throws RemoteException;
+
public void onDisconnected() {
- IAccountManagerResponse response = close();
+ mAuthenticator = null;
+ IAccountManagerResponse response = getResponseAndClose();
if (response != null) {
- onError(3, "disconnected");
+ onError(Constants.ERROR_CODE_REMOTE_EXCEPTION,
+ "disconnected");
}
}
public void onTimedOut() {
- IAccountManagerResponse response = close();
+ IAccountManagerResponse response = getResponseAndClose();
if (response != null) {
- onError(4, "timeout");
+ onError(Constants.ERROR_CODE_REMOTE_EXCEPTION,
+ "timeout");
}
}
- public void onIntResult(int result) throws RemoteException {
- IAccountManagerResponse response = close();
- if (response != null) {
- response.onIntResult(result);
+ public void onResult(Bundle result) {
+ mNumResults++;
+ if (result != null && !TextUtils.isEmpty(result.getString(Constants.AUTHTOKEN_KEY))) {
+ cancelNotification();
}
- }
-
- public void onBooleanResult(boolean result) throws RemoteException {
- IAccountManagerResponse response = close();
- if (response != null) {
- response.onBooleanResult(result);
+ IAccountManagerResponse response;
+ if (mExpectActivityLaunch && result != null
+ && result.containsKey(Constants.INTENT_KEY)) {
+ response = mResponse;
+ } else {
+ response = getResponseAndClose();
}
- }
-
- public void onStringResult(String result) throws RemoteException {
- IAccountManagerResponse response = close();
- if (response != null) {
- response.onStringResult(result);
- }
- }
-
- public void onError(int errorCode, String errorMessage) {
- IAccountManagerResponse response = close();
if (response != null) {
try {
- response.onError(errorCode, errorMessage);
+ if (result == null) {
+ response.onError(Constants.ERROR_CODE_INVALID_RESPONSE,
+ "null bundle returned");
+ } else {
+ response.onResult(result);
+ }
} catch (RemoteException e) {
- // error while trying to notify user of an error
+ // 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);
+ }
}
}
}
- }
- private class GetAuthTokenSession extends Session {
- final Account mAccount;
- final String mAuthTokenType;
- final boolean mNotifyOnAuthFailure;
-
- public GetAuthTokenSession(IAccountManagerResponse response,
- Account account, String authTokenType, boolean interactive) {
- super(response, account.mType);
- mAccount = account;
- mAuthTokenType = authTokenType;
- mNotifyOnAuthFailure = interactive;
- }
-
- public void onConnected(IBinder service) {
- super.onConnected(service);
-
- try {
- mAuthenticator.getAuthToken(this, mAccount.mName, mAccount.mType, mAuthTokenType);
- } catch (RemoteException e) {
- onError(4, "remote exception");
- }
- }
-
- public void onStringResult(String result) throws RemoteException {
- IAccountManagerResponse response = close();
- if (response != null) {
- cacheAuthToken(mAccount, mAccountType, result);
- response.onStringResult(result);
- }
+ public void onRequestContinued() {
+ mNumRequestContinued++;
}
public void onError(int errorCode, String errorMessage) {
- if (mNotifyOnAuthFailure && errorCode == 0 /* TODO: put the real value here */) {
- // TODO: authentication failed, pop up the notification
+ mNumErrors++;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Session.onError: " + errorCode + ", " + errorMessage);
}
- super.onError(errorCode, errorMessage);
- }
- }
-
- private class CheckUsernameExistenceSession extends Session {
- final String mUsername;
-
- public CheckUsernameExistenceSession(IAccountManagerResponse response,
- String username, String accountType) {
- super(response, accountType);
- mUsername = username;
- }
-
- public void onConnected(IBinder service) {
- super.onConnected(service);
-
- try {
- mAuthenticator.checkUsernameExistence(this, mAccountType, mUsername);
- } catch (RemoteException e) {
- onError(4, "remote exception");
- }
- }
- }
-
- private class AddAccountInteractivelySession extends Session {
-
- public AddAccountInteractivelySession(IAccountManagerResponse response,
- String accountType) {
- super(response, accountType);
- }
-
- public void onConnected(IBinder service) {
- super.onConnected(service);
-
- try {
- mAuthenticator.addAccount(this, mAccountType);
- } catch (RemoteException e) {
- onError(4, "remote exception");
- }
- }
- }
-
- private class AuthenticateAccountSession extends Session {
- final String mUsername;
- final String mPassword;
-
- public AuthenticateAccountSession(IAccountManagerResponse response, Account account,
- String password) {
- super(response, account.mType);
- mUsername = account.mName;
- mPassword = password;
- }
-
- public void onConnected(IBinder service) {
- super.onConnected(service);
-
- try {
- mAuthenticator.authenticateAccount(this, mUsername, mAccountType, mPassword);
- } catch (RemoteException e) {
- onError(4, "remote exception");
- }
- }
- }
-
- private class UpdatePasswordSession extends Session {
- final String mUsername;
-
- public UpdatePasswordSession(IAccountManagerResponse response, Account account) {
- super(response, account.mType);
- mUsername = account.mName;
- }
-
- public void onConnected(IBinder service) {
- super.onConnected(service);
-
- try {
- mAuthenticator.updatePassword(this, mUsername, mAccountType);
- } catch (RemoteException e) {
- onError(4, "remote exception");
- }
- }
- }
-
- private class EditPropertiesSession extends Session {
- public EditPropertiesSession(IAccountManagerResponse response, String accountType) {
- super(response, accountType);
- }
-
- public void onConnected(IBinder service) {
- super.onConnected(service);
-
- try {
- mAuthenticator.editProperties(this, mAccountType);
- } catch (RemoteException e) {
- onError(4, "remote exception");
- }
- }
- }
-
- private class GetPasswordStrengthSession extends Session {
- final String mPassword;
-
- public GetPasswordStrengthSession(IAccountManagerResponse response,
- String accountType, String password) {
- super(response, accountType);
- mPassword = password;
- }
-
- public void onConnected(IBinder service) {
- super.onConnected(service);
-
- try {
- mAuthenticator.getPasswordStrength(this, mAccountType, mPassword);
- } catch (RemoteException e) {
- onError(4, "remote exception");
+ IAccountManagerResponse response = getResponseAndClose();
+ if (response != null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Session.onError: responding");
+ }
+ try {
+ response.onError(errorCode, errorMessage);
+ } catch (RemoteException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
+ }
+ }
+ } else {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Session.onError: already closed");
+ }
}
}
}
@@ -807,18 +879,32 @@ public class AccountManagerService extends IAccountManager.Stub {
db.execSQL("CREATE TABLE " + TABLE_META + " ( "
+ META_KEY + " TEXT PRIMARY KEY NOT NULL, "
+ META_VALUE + " TEXT)");
+
+ db.execSQL(""
+ + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+ + " BEGIN"
+ + " DELETE FROM " + TABLE_AUTHTOKENS
+ + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " DELETE FROM " + TABLE_EXTRAS
+ + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " END");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- Log.e(TAG, "GLS upgrade from version " + oldVersion + " to version " +
- newVersion + " not supported");
+ Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
- db.execSQL("DROP TABLE " + TABLE_ACCOUNTS);
- db.execSQL("DROP TABLE " + TABLE_AUTHTOKENS);
- db.execSQL("DROP TABLE " + TABLE_EXTRAS);
- db.execSQL("DROP TABLE " + TABLE_META);
- onCreate(db);
+ if (oldVersion == 1) {
+ db.execSQL(""
+ + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+ + " BEGIN"
+ + " DELETE FROM " + TABLE_AUTHTOKENS
+ + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + " =OLD." + ACCOUNTS_ID + " ;"
+ + " DELETE FROM " + TABLE_EXTRAS
+ + " WHERE " + EXTRAS_ACCOUNTS_ID + " =OLD." + ACCOUNTS_ID + " ;"
+ + " END");
+ oldVersion++;
+ }
}
@Override
@@ -898,4 +984,36 @@ public class AccountManagerService extends IAccountManager.Stub {
public IBinder onBind(Intent intent) {
return asBinder();
}
+
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ synchronized (mSessions) {
+ final long now = SystemClock.elapsedRealtime();
+ fout.println("AccountManagerService: " + mSessions.size() + " sessions");
+ for (Session session : mSessions.values()) {
+ fout.println(" " + session.toDebugString(now));
+ }
+ }
+
+ fout.println();
+
+ mAuthenticatorCache.dump(fd, fout, args);
+ }
+
+ private void doNotification(CharSequence message, Intent intent) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "doNotification: " + message + " intent:" + intent);
+ }
+
+ // TODO(fredq) add this back in when we fix permissions
+// Notification n = new Notification(android.R.drawable.stat_sys_warning, null, 0 /* when */);
+// n.setLatestEventInfo(mContext, mContext.getText(R.string.notification_title), message,
+// PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
+// ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
+// .notify(NOTIFICATION_ID, n);
+ }
+
+ private void cancelNotification() {
+// ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
+// .cancel(NOTIFICATION_ID);
+ }
}
diff --git a/core/java/android/accounts/AccountMonitor.java b/core/java/android/accounts/AccountMonitor.java
index 4ad06d493b944..38032c59ba661 100644
--- a/core/java/android/accounts/AccountMonitor.java
+++ b/core/java/android/accounts/AccountMonitor.java
@@ -60,7 +60,7 @@ public class AccountMonitor extends BroadcastReceiver {
// Register a broadcast receiver to monitor account changes
IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(AccountsServiceConstants.LOGIN_ACCOUNTS_CHANGED_ACTION);
+ intentFilter.addAction(Constants.LOGIN_ACCOUNTS_CHANGED_ACTION);
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); // To recover from disk-full.
mContext.registerReceiver(this, intentFilter);
@@ -77,15 +77,23 @@ public class AccountMonitor extends BroadcastReceiver {
notifyListener();
}
- private synchronized void notifyListener() {
- AccountManager accountManager =
- (AccountManager)mContext.getSystemService(Context.ACCOUNT_SERVICE);
- Account[] accounts = accountManager.blockingGetAccounts();
- String[] accountNames = new String[accounts.length];
- for (int i = 0; i < accounts.length; i++) {
- accountNames[i] = accounts[i].mName;
+ private Future1Callback mGetAccountsCallback = new Future1Callback() {
+ public void run(Future1 future) {
+ try {
+ Account[] accounts = future.getResult();
+ String[] accountNames = new String[accounts.length];
+ for (int i = 0; i < accounts.length; i++) {
+ accountNames[i] = accounts[i].mName;
+ }
+ mListener.onAccountsUpdated(accountNames);
+ } catch (OperationCanceledException e) {
+ // the request was canceled
+ }
}
- mListener.onAccountsUpdated(accountNames);
+ };
+
+ private synchronized void notifyListener() {
+ AccountManager.get(mContext).getAccounts(mGetAccountsCallback, null /* handler */);
}
/**
diff --git a/core/java/android/accounts/AccountsServiceConstants.java b/core/java/android/accounts/AccountsServiceConstants.java
deleted file mode 100644
index b882e7b3ceb73..0000000000000
--- a/core/java/android/accounts/AccountsServiceConstants.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2008 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.content.Intent;
-
-/**
- * Miscellaneous constants used by the AccountsService and its
- * clients.
- */
-// TODO: These constants *could* come directly from the
-// IAccountsService interface, but that's not possible since the
-// aidl compiler doesn't let you define constants (yet.)
-public class AccountsServiceConstants {
- /** This class is never instantiated. */
- private AccountsServiceConstants() {
- }
-
- /**
- * Action sent as a broadcast Intent by the AccountsService
- * when accounts are added to and/or removed from the device's
- * database, or when the primary account is changed.
- */
- public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
- "android.accounts.LOGIN_ACCOUNTS_CHANGED";
-
- /**
- * Action sent as a broadcast Intent by the AccountsService
- * when it starts up and no accounts are available (so some should be added).
- */
- public static final String LOGIN_ACCOUNTS_MISSING_ACTION =
- "android.accounts.LOGIN_ACCOUNTS_MISSING";
-
- /**
- * Action on the intent used to bind to the IAccountsService interface. This
- * is used for services that have multiple interfaces (allowing
- * them to differentiate the interface intended, and return the proper
- * Binder.)
- */
- private static final String ACCOUNTS_SERVICE_ACTION = "android.accounts.IAccountsService";
-
- /*
- * The intent uses a component in addition to the action to ensure the actual
- * accounts service is bound to (a malicious third-party app could
- * theoretically have a service with the same action).
- */
- /** The intent used to bind to the accounts service. */
- public static final Intent SERVICE_INTENT =
- new Intent()
- .setClassName("com.google.android.googleapps",
- "com.google.android.googleapps.GoogleLoginService")
- .setAction(ACCOUNTS_SERVICE_ACTION);
-
- /**
- * Checks whether the intent is to bind to the accounts service.
- *
- * @param bindIntent The Intent used to bind to the service.
- * @return Whether the intent is to bind to the accounts service.
- */
- public static final boolean isForAccountsService(Intent bindIntent) {
- String otherAction = bindIntent.getAction();
- return otherAction != null && otherAction.equals(ACCOUNTS_SERVICE_ACTION);
- }
-}
diff --git a/core/java/android/accounts/AuthenticatorBindHelper.java b/core/java/android/accounts/AuthenticatorBindHelper.java
index ec418108d962f..6c28485c8ad99 100644
--- a/core/java/android/accounts/AuthenticatorBindHelper.java
+++ b/core/java/android/accounts/AuthenticatorBindHelper.java
@@ -16,19 +16,20 @@
package android.accounts;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.ServiceConnection;
-import android.content.Intent;
+import android.util.Log;
-import java.util.Map;
import java.util.ArrayList;
+import java.util.Map;
-import com.google.android.collect.Maps;
import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
/**
* A helper object that simplifies binding to Account Authenticators. It uses the
@@ -39,13 +40,14 @@ import com.google.android.collect.Lists;
* itself succeeds, even if the authenticator is already bound internally.
*/
public class AuthenticatorBindHelper {
- final private Handler mHandler;
- final private Context mContext;
- final private int mMessageWhatConnected;
- final private int mMessageWhatDisconnected;
- final private Map mServiceConnections = Maps.newHashMap();
- final private Map> mServiceUsers = Maps.newHashMap();
- final private AccountAuthenticatorCache mAuthenticatorCache;
+ private static final String TAG = "Accounts";
+ private final Handler mHandler;
+ private final Context mContext;
+ private final int mMessageWhatConnected;
+ private final int mMessageWhatDisconnected;
+ private final Map mServiceConnections = Maps.newHashMap();
+ private final Map> mServiceUsers = Maps.newHashMap();
+ private final AccountAuthenticatorCache mAuthenticatorCache;
public AuthenticatorBindHelper(Context context,
AccountAuthenticatorCache authenticatorCache, Handler handler,
@@ -66,15 +68,39 @@ public class AuthenticatorBindHelper {
// if the authenticator is connecting or connected then return true
synchronized (mServiceConnections) {
if (mServiceConnections.containsKey(authenticatorType)) {
+ MyServiceConnection connection = mServiceConnections.get(authenticatorType);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "service connection already exists for " + authenticatorType);
+ }
mServiceUsers.get(authenticatorType).add(callback);
+ if (connection.mService != null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "the service is connected, scheduling a connected message for "
+ + authenticatorType);
+ }
+ connection.scheduleCallbackConnectedMessage(callback);
+ } else {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "the service is *not* connected, waiting for for "
+ + authenticatorType);
+ }
+ }
return true;
}
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "there is no service connection for " + authenticatorType);
+ }
+
// otherwise find the component name for the authenticator and initiate a bind
// if no authenticator or the bind fails then return false, otherwise return true
AccountAuthenticatorCache.AuthenticatorInfo authenticatorInfo =
mAuthenticatorCache.getAuthenticatorInfo(authenticatorType);
if (authenticatorInfo == null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "there is no authenticator for " + authenticatorType
+ + ", bailing out");
+ }
return false;
}
@@ -83,7 +109,13 @@ public class AuthenticatorBindHelper {
Intent intent = new Intent();
intent.setAction("android.accounts.AccountAuthenticator");
intent.setComponent(authenticatorInfo.mComponentName);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "performing bindService to " + authenticatorInfo.mComponentName);
+ }
if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "bindService to " + authenticatorInfo.mComponentName + " failed");
+ }
return false;
}
@@ -94,24 +126,43 @@ public class AuthenticatorBindHelper {
}
public void unbind(Callback callbackToUnbind) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "unbinding callback " + callbackToUnbind);
+ }
synchronized (mServiceConnections) {
for (Map.Entry> entry : mServiceUsers.entrySet()) {
final String authenticatorType = entry.getKey();
final ArrayList serviceUsers = entry.getValue();
for (Callback callback : serviceUsers) {
if (callback == callbackToUnbind) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "found callback in service" + authenticatorType);
+ }
serviceUsers.remove(callbackToUnbind);
if (serviceUsers.isEmpty()) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "there are no more callbacks for service "
+ + authenticatorType + ", unbinding service");
+ }
unbindFromService(authenticatorType);
+ } else {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "leaving service " + authenticatorType
+ + " around since there are still callbacks using it");
+ }
}
return;
}
}
}
+ Log.e(TAG, "did not find callback " + callbackToUnbind + " in any of the services");
}
}
private void unbindFromService(String authenticatorType) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "unbindService from " + authenticatorType);
+ }
mContext.unbindService(mServiceConnections.get(authenticatorType));
mServiceUsers.remove(authenticatorType);
mServiceConnections.remove(authenticatorType);
@@ -127,28 +178,49 @@ public class AuthenticatorBindHelper {
}
private class MyServiceConnection implements ServiceConnection {
- final private String mAuthenticatorType;
+ private final String mAuthenticatorType;
+ private IBinder mService = null;
public MyServiceConnection(String authenticatorType) {
mAuthenticatorType = authenticatorType;
}
public void onServiceConnected(ComponentName name, IBinder service) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "onServiceConnected for account type " + mAuthenticatorType);
+ }
// post a message for each service user to tell them that the service is connected
synchronized (mServiceConnections) {
+ mService = service;
for (Callback callback : mServiceUsers.get(mAuthenticatorType)) {
- final ConnectedMessagePayload payload =
- new ConnectedMessagePayload(service, callback);
- mHandler.obtainMessage(mMessageWhatConnected, payload).sendToTarget();
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "the service became connected, scheduling a connected "
+ + "message for " + mAuthenticatorType);
+ }
+ scheduleCallbackConnectedMessage(callback);
}
}
}
+ private void scheduleCallbackConnectedMessage(Callback callback) {
+ final ConnectedMessagePayload payload =
+ new ConnectedMessagePayload(mService, callback);
+ mHandler.obtainMessage(mMessageWhatConnected, payload).sendToTarget();
+ }
+
public void onServiceDisconnected(ComponentName name) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "onServiceDisconnected for account type " + mAuthenticatorType);
+ }
// post a message for each service user to tell them that the service is disconnected,
// and unbind from the service.
synchronized (mServiceConnections) {
for (Callback callback : mServiceUsers.get(mAuthenticatorType)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "the service became disconnected, scheduling a "
+ + "disconnected message for "
+ + mAuthenticatorType);
+ }
mHandler.obtainMessage(mMessageWhatDisconnected, callback).sendToTarget();
}
unbindFromService(mAuthenticatorType);
@@ -159,10 +231,16 @@ public class AuthenticatorBindHelper {
boolean handleMessage(Message message) {
if (message.what == mMessageWhatConnected) {
ConnectedMessagePayload payload = (ConnectedMessagePayload)message.obj;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "notifying callback " + payload.mCallback + " that it is connected");
+ }
payload.mCallback.onConnected(payload.mService);
return true;
} else if (message.what == mMessageWhatDisconnected) {
Callback callback = (Callback)message.obj;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "notifying callback " + callback + " that it is disconnected");
+ }
callback.onDisconnected();
return true;
} else {
diff --git a/core/java/android/accounts/AccountManagerResponse.java b/core/java/android/accounts/AuthenticatorException.java
similarity index 60%
rename from core/java/android/accounts/AccountManagerResponse.java
rename to core/java/android/accounts/AuthenticatorException.java
index b15fb131a0c06..40234945cfbe3 100644
--- a/core/java/android/accounts/AccountManagerResponse.java
+++ b/core/java/android/accounts/AuthenticatorException.java
@@ -13,20 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package android.accounts;
-/**
- * Object that wraps calls to an {@link IAccountManagerResponse} object.
- * TODO: this interface is still in flux
- */
-public class AccountManagerResponse {
- private IAccountManagerResponse mResponse;
-
- public AccountManagerResponse(IAccountManagerResponse accountManagerResponse) {
- mResponse = accountManagerResponse;
+public class AuthenticatorException extends Exception {
+ public AuthenticatorException() {
+ super();
}
-
- public IAccountManagerResponse getIAccountManagerResponse() {
- return mResponse;
+ public AuthenticatorException(String message) {
+ super(message);
+ }
+ public AuthenticatorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public AuthenticatorException(Throwable cause) {
+ super(cause);
}
}
diff --git a/core/java/android/accounts/Constants.java b/core/java/android/accounts/Constants.java
new file mode 100644
index 0000000000000..d3b6aa0f0b5cc
--- /dev/null
+++ b/core/java/android/accounts/Constants.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+public class Constants {
+ // this should never be instantiated
+ private Constants() {}
+
+ public static final int ERROR_CODE_REMOTE_EXCEPTION = 1;
+ public static final int ERROR_CODE_NETWORK_ERROR = 3;
+ 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 String ACCOUNTS_KEY = "accounts";
+ public static final String AUTHENTICATOR_TYPES_KEY = "authenticator_types";
+ public static final String PASSWORD_KEY = "password";
+ public static final String USERDATA_KEY = "userdata";
+ public static final String AUTHTOKEN_KEY = "authtoken";
+ public static final String ACCOUNT_NAME_KEY = "authAccount";
+ public static final String ACCOUNT_TYPE_KEY = "accountType";
+ public static final String ERROR_CODE_KEY = "errorCode";
+ public static final String ERROR_MESSAGE_KEY = "errorMessage";
+ 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 AUTH_FAILED_MESSAGE_KEY = "authFailedMessage";
+ /**
+ * Action sent as a broadcast Intent by the AccountsService
+ * when accounts are added to and/or removed from the device's
+ * database, or when the primary account is changed.
+ */
+ public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
+ "android.accounts.LOGIN_ACCOUNTS_CHANGED";
+}
diff --git a/core/java/android/accounts/Future1.java b/core/java/android/accounts/Future1.java
new file mode 100644
index 0000000000000..386cb6ec2ad06
--- /dev/null
+++ b/core/java/android/accounts/Future1.java
@@ -0,0 +1,46 @@
+/*
+ * 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 java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An extension of {@link Future} that provides wrappers for {@link #get()} that handle the various
+ * exceptions that {@link #get()} may return and rethrows them as exceptions specific to
+ * {@link AccountManager}.
+ */
+public interface Future1 extends Future {
+ /**
+ * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the
+ * {@link Future1} is canceled and {@link OperationCanceledException} is thrown.
+ * @return the {@link android.os.Bundle} that is returned by get()
+ * @throws OperationCanceledException if get() throws the unchecked CancellationException
+ * or if the Future was interrupted.
+ */
+ V getResult() throws OperationCanceledException;
+
+ /**
+ * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the
+ * {@link Future1} is canceled and {@link OperationCanceledException} is thrown.
+ * @param timeout the maximum time to wait
+ * @param unit the time unit of the timeout argument
+ * @return the {@link android.os.Bundle} that is returned by {@link Future#get()}
+ * @throws OperationCanceledException if get() throws the unchecked
+ * {@link java.util.concurrent.CancellationException} or if the {@link Future1} was interrupted.
+ */
+ V getResult(long timeout, TimeUnit unit) throws OperationCanceledException;
+}
\ No newline at end of file
diff --git a/core/java/android/accounts/Future1Callback.java b/core/java/android/accounts/Future1Callback.java
new file mode 100644
index 0000000000000..886671ba6b36b
--- /dev/null
+++ b/core/java/android/accounts/Future1Callback.java
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+public interface Future1Callback {
+ void run(Future1 future);
+}
diff --git a/core/java/android/accounts/Future2.java b/core/java/android/accounts/Future2.java
new file mode 100644
index 0000000000000..b2ea84f493060
--- /dev/null
+++ b/core/java/android/accounts/Future2.java
@@ -0,0 +1,57 @@
+/*
+ * 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 java.io.IOException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An extension of {@link Future} that provides wrappers for {@link #get()} that handle the various
+ * exceptions that {@link #get()} may return and rethrows them as exceptions specific to
+ * {@link AccountManager}.
+ */
+public interface Future2 extends Future {
+ /**
+ * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the
+ * {@link Future2} is canceled and {@link OperationCanceledException} is thrown.
+ * @return the {@link android.os.Bundle} that is returned by {@link Future#get()}
+ * @throws OperationCanceledException if get() throws the unchecked
+ * {@link java.util.concurrent.CancellationException} or if the {@link Future2} was interrupted.
+ * @throws IOException if the request was unable to complete due to a network error
+ * @throws AuthenticatorException if there was an error communicating with the
+ * {@link AbstractAccountAuthenticator}.
+ */
+ Bundle getResult()
+ throws OperationCanceledException, IOException, AuthenticatorException;
+
+ /**
+ * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the
+ * {@link Future2} is canceled and {@link OperationCanceledException} is thrown.
+ * @param timeout the maximum time to wait
+ * @param unit the time unit of the timeout argument
+ * @return the {@link android.os.Bundle} that is returned by {@link Future#get()}
+ * @throws OperationCanceledException if get() throws the unchecked
+ * {@link java.util.concurrent.CancellationException} or if the {@link Future2} was interrupted.
+ * @throws IOException if the request was unable to complete due to a network error
+ * @throws AuthenticatorException if there was an error communicating with the
+ * {@link AbstractAccountAuthenticator}.
+ */
+ Bundle getResult(long timeout, TimeUnit unit)
+ throws OperationCanceledException, IOException, AuthenticatorException;
+}
diff --git a/core/java/android/accounts/Future2Callback.java b/core/java/android/accounts/Future2Callback.java
new file mode 100644
index 0000000000000..7ef0c94741d09
--- /dev/null
+++ b/core/java/android/accounts/Future2Callback.java
@@ -0,0 +1,20 @@
+/*
+ * 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;
+
+public interface Future2Callback {
+ void run(Future2 future);
+}
\ No newline at end of file
diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl
index b2c222343bc57..70c075208cb73 100644
--- a/core/java/android/accounts/IAccountAuthenticator.aidl
+++ b/core/java/android/accounts/IAccountAuthenticator.aidl
@@ -17,6 +17,8 @@
package android.accounts;
import android.accounts.IAccountAuthenticatorResponse;
+import android.accounts.Account;
+import android.os.Bundle;
/**
* Service that allows the interaction with an authentication server.
@@ -25,36 +27,32 @@ oneway interface IAccountAuthenticator {
/**
* prompts the user for account information and adds the result to the IAccountManager
*/
- void addAccount(in IAccountAuthenticatorResponse response, String accountType);
+ void addAccount(in IAccountAuthenticatorResponse response, String accountType,
+ String authTokenType, in Bundle options);
+
+ /**
+ * Checks that the account/password combination is valid.
+ * @deprecated
+ */
+ void confirmPassword(in IAccountAuthenticatorResponse response,
+ in Account account, String password);
/**
* prompts the user for the credentials of the account
*/
- void authenticateAccount(in IAccountAuthenticatorResponse response, String name,
- String type, String password);
+ void confirmCredentials(in IAccountAuthenticatorResponse response, in Account account);
/**
* gets the password by either prompting the user or querying the IAccountManager
*/
- void getAuthToken(in IAccountAuthenticatorResponse response,
- String name, String type, String authTokenType);
-
- /**
- * does local analysis or uses a service in the cloud
- */
- void getPasswordStrength(in IAccountAuthenticatorResponse response,
- String accountType, String password);
-
- /**
- * checks with the login service in the cloud
- */
- void checkUsernameExistence(in IAccountAuthenticatorResponse response,
- String accountType, String username);
+ void getAuthToken(in IAccountAuthenticatorResponse response, in Account account,
+ String authTokenType, in Bundle options);
/**
* prompts the user for a new password and writes it to the IAccountManager
*/
- void updatePassword(in IAccountAuthenticatorResponse response, String name, String type);
+ void updateCredentials(in IAccountAuthenticatorResponse response, in Account account,
+ String authTokenType, in Bundle options);
/**
* launches an activity that lets the user edit and set the properties for an authenticator
diff --git a/core/java/android/accounts/IAccountAuthenticatorResponse.aidl b/core/java/android/accounts/IAccountAuthenticatorResponse.aidl
index 83504d3304f2f..a9ac2f1bb0802 100644
--- a/core/java/android/accounts/IAccountAuthenticatorResponse.aidl
+++ b/core/java/android/accounts/IAccountAuthenticatorResponse.aidl
@@ -15,14 +15,13 @@
*/
package android.accounts;
-import android.os.Parcelable;
+import android.os.Bundle;
/**
* The interface used to return responses from an {@link IAccountAuthenticator}
*/
oneway interface IAccountAuthenticatorResponse {
- void onIntResult(int result);
- void onBooleanResult(boolean result);
- void onStringResult(String result);
+ void onResult(in Bundle value);
+ void onRequestContinued();
void onError(int errorCode, String errorMessage);
}
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 8a0d1c45906af..365a92a05cbb5 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -26,6 +26,7 @@ import android.os.Bundle;
interface IAccountManager {
String getPassword(in Account account);
String getUserData(in Account account, String key);
+ String[] getAuthenticatorTypes();
Account[] getAccounts();
Account[] getAccountsByType(String accountType);
boolean addAccount(in Account account, String password, in Bundle extras);
@@ -37,19 +38,21 @@ interface IAccountManager {
void clearPassword(in Account account);
void setUserData(in Account account, String key, String value);
- // interactive
+ void getAuthToken(in IAccountManagerResponse response, in Account account,
+ String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch,
+ in Bundle options);
+ void addAcount(in IAccountManagerResponse response, String accountType,
+ String authTokenType, 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 getAuthToken(in IAccountManagerResponse response, in Account account, String authTokenType,
- boolean notifyOnAuthFailure);
- void addAccountInteractively(in IAccountManagerResponse response, String accountType);
- void authenticateAccount(in IAccountManagerResponse response, in Account account,
+ /*
+ * @Deprecated
+ */
+ void confirmPassword(in IAccountManagerResponse response, in Account account,
String password);
- void updatePassword(in IAccountManagerResponse response, in Account account);
- void editProperties(in IAccountManagerResponse response, String accountType);
-
- // not interactive
- void getPasswordStrength(in IAccountManagerResponse response, String accountType,
- String password);
- void checkUsernameExistence(in IAccountManagerResponse response, String accountType,
- String username);
}
diff --git a/core/java/android/accounts/IAccountManagerResponse.aidl b/core/java/android/accounts/IAccountManagerResponse.aidl
index 7bbaa8b20f54f..52f21bc6b322d 100644
--- a/core/java/android/accounts/IAccountManagerResponse.aidl
+++ b/core/java/android/accounts/IAccountManagerResponse.aidl
@@ -15,13 +15,12 @@
*/
package android.accounts;
+import android.os.Bundle;
/**
* The interface used to return responses for asynchronous calls to the {@link IAccountManager}
*/
oneway interface IAccountManagerResponse {
- void onStringResult(String value);
- void onIntResult(int value);
- void onBooleanResult(boolean value);
- void onError(int errorCode, String errorMessage);
+ void onResult(in Bundle value);
+ void onError(int errorCode, String errorMessage);
}
diff --git a/core/java/android/accounts/NetworkErrorException.java b/core/java/android/accounts/NetworkErrorException.java
new file mode 100644
index 0000000000000..f855cc802915b
--- /dev/null
+++ b/core/java/android/accounts/NetworkErrorException.java
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+public class NetworkErrorException extends Exception {
+ public NetworkErrorException() {
+ super();
+ }
+ public NetworkErrorException(String message) {
+ super(message);
+ }
+ public NetworkErrorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public NetworkErrorException(Throwable cause) {
+ super(cause);
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/accounts/OperationCanceledException.java b/core/java/android/accounts/OperationCanceledException.java
new file mode 100644
index 0000000000000..2f2c1646caf36
--- /dev/null
+++ b/core/java/android/accounts/OperationCanceledException.java
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+public class OperationCanceledException extends Exception {
+ public OperationCanceledException() {
+ super();
+ }
+ public OperationCanceledException(String message) {
+ super(message);
+ }
+ public OperationCanceledException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public OperationCanceledException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8150a96be7c31..29aa871b6100b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -129,7 +129,7 @@
Voice/SMS service is blocked.
All voice/SMS services are blocked.
-
+
Voice
@@ -206,6 +206,13 @@
Too many requests are being processed. Try again later.
+
+
+ Sign-in error
+
Sync
@@ -276,7 +283,7 @@
Android System
-
+
Services that cost you money
@@ -1292,7 +1299,7 @@
Confirm
-
+
Do you want the browser to remember this password?
@@ -1320,8 +1327,8 @@
delete
-
-
Search
@@ -1383,7 +1390,7 @@
- tomorrow
- in %d days
-
+
- 1 sec ago
@@ -1511,7 +1518,7 @@
"%m/%d/%Y"
-
"%1$s, %2$s, %3$s \u2013 %4$s, %5$s, %6$s"
@@ -1519,7 +1526,7 @@
Example: "Mon, Dec 31, 2007 - Tue, Jan 1, 2008" -->
"%1$s, %2$s \u2013 %4$s, %5$s"
-
"%2$s, %3$s \u2013 %5$s, %6$s"
@@ -2216,7 +2223,7 @@
Mount
Don\'t mount
-
+
There is a problem using your SD card for USB storage.
USB connected
@@ -2237,7 +2244,7 @@
Turn Off
Cancel
-
+
We've encountered a problem turning off USB storage. Check to make sure you have unmounted the USB host, then try again.
@@ -2258,10 +2265,10 @@
Select Input Method
-
+
\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ
\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
-
+
candidates
@@ -2308,23 +2315,23 @@
Go
-
+
Search
-
+
Send
-
+
Next
-
+
Done
-
+
Execute
-
-
@@ -2337,7 +2344,7 @@
-
+