Merge branch 'readonly-p4-master'
This commit is contained in:
committed by
The Android Open Source Project
commit
0c9c41255e
1838
api/current.xml
1838
api/current.xml
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
102
core/java/android/accounts/AccountAuthenticatorActivity.java
Normal file
102
core/java/android/accounts/AccountAuthenticatorActivity.java
Normal file
@@ -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:
|
||||
* <p>
|
||||
* intent.putExtra(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY, response);
|
||||
* <p>
|
||||
* The activity that it launches should extend the AccountAuthenticatorActivity. If this
|
||||
* activity has a result that satisfies the original request it sets it via:
|
||||
* <p>
|
||||
* setAccountAuthenticatorResult(result)
|
||||
* <p>
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -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<String, AuthenticatorInfo> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<AccountAuthenticatorResponse> CREATOR =
|
||||
new Creator<AccountAuthenticatorResponse>() {
|
||||
public AccountAuthenticatorResponse createFromParcel(Parcel source) {
|
||||
return new AccountAuthenticatorResponse(source);
|
||||
}
|
||||
|
||||
public AccountAuthenticatorResponse[] newArray(int size) {
|
||||
return new AccountAuthenticatorResponse[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
*
|
||||
* <p>
|
||||
* 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<String> getPassword(final Future1Callback<String> callback,
|
||||
final Account account, final Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<String>() {
|
||||
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<String> getUserData(Future1Callback<String> callback,
|
||||
final Account account, final String key, Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<String>() {
|
||||
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<String[]> getAuthenticatorTypes(Future1Callback<String[]> callback,
|
||||
Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<String[]>() {
|
||||
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<Account[]> getAccounts(Future1Callback<Account[]> callback, Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<Account[]>() {
|
||||
public Account[] call() throws Exception {
|
||||
return blockingGetAccounts();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Future1<Account[]> getAccountsByType(Future1Callback<Account[]> callback,
|
||||
final String type, Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<Account[]>() {
|
||||
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<Boolean> addAccountExplicitly(final Future1Callback<Boolean> callback,
|
||||
final Account account, final String password, final Bundle extras,
|
||||
final Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<Boolean>() {
|
||||
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<Void> removeAccount(Future1Callback<Void> callback, final Account account,
|
||||
final Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<Void>() {
|
||||
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<Void> invalidateAuthToken(Future1Callback<Void> callback,
|
||||
final String accountType, final String authToken, final Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<Void>() {
|
||||
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<String> peekAuthToken(Future1Callback<String> callback,
|
||||
final Account account, final String authTokenType, final Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<String>() {
|
||||
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<Void> setPassword(Future1Callback<Void> callback,
|
||||
final Account account, final String password, final Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<Void>() {
|
||||
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<Void> clearPassword(final Future1Callback<Void> callback, final Account account,
|
||||
final Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<Void>() {
|
||||
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<Void> setUserData(Future1Callback<Void> callback,
|
||||
final Account account, final String key, final String value, final Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<Void>() {
|
||||
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<Void> setAuthToken(Future1Callback<Void> callback,
|
||||
final Account account, final String authTokenType, final String authToken,
|
||||
final Handler handler) {
|
||||
return startAsFuture(callback, handler, new Callable<Void>() {
|
||||
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<Boolean> confirmPassword(final Account account, final String password,
|
||||
Future1Callback<Boolean> 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 <V> void postToHandler(Handler handler, final Future1Callback<V> callback,
|
||||
final Future1<V> 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 <V> Future1<V> startAsFuture(Future1Callback<V> callback, Handler handler,
|
||||
Callable<V> callable) {
|
||||
final FutureTaskWithCallback<V> task =
|
||||
new FutureTaskWithCallback<V>(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<V> extends FutureTask<V> implements Future1<V> {
|
||||
final Future1Callback<V> mCallback;
|
||||
final Handler mHandler;
|
||||
|
||||
mService.authenticateAccount(response, mAccount, mPassword);
|
||||
} catch (RemoteException e) {
|
||||
// if this happens the entire runtime will restart
|
||||
public FutureTaskWithCallback(Future1Callback<V> callback, Callable<V> 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<Bundle> 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<Bundle>() {
|
||||
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<Boolean> implements Future1<Boolean> {
|
||||
final IAccountManagerResponse response;
|
||||
final Handler mHandler;
|
||||
final Future1Callback<Boolean> mCallback;
|
||||
public AMSTaskBoolean(Handler handler, Future1Callback<Boolean> callback) {
|
||||
super(new Callable<Boolean>() {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, Session> mSessions = new LinkedHashMap<String, Session>();
|
||||
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<AccountAuthenticatorCache.AuthenticatorInfo> 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<AuthTokenKey> keysToRemove = Lists.newArrayList();
|
||||
for (AuthTokenKey key : mAuthTokenCache.keySet()) {
|
||||
if (key.mAccount.equals(account)) {
|
||||
keysToRemove.add(key);
|
||||
}
|
||||
}
|
||||
for (AuthTokenKey key : keysToRemove) {
|
||||
mAuthTokenCache.remove(key);
|
||||
}
|
||||
|
||||
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
||||
db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
|
||||
new String[]{account.mName, account.mType});
|
||||
mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Account[]> mGetAccountsCallback = new Future1Callback<Account[]>() {
|
||||
public void run(Future1<Account[]> 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 */);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<String, MyServiceConnection> mServiceConnections = Maps.newHashMap();
|
||||
final private Map<String, ArrayList<Callback>> 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<String, MyServiceConnection> mServiceConnections = Maps.newHashMap();
|
||||
private final Map<String, ArrayList<Callback>> 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<String, ArrayList<Callback>> entry : mServiceUsers.entrySet()) {
|
||||
final String authenticatorType = entry.getKey();
|
||||
final ArrayList<Callback> 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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
48
core/java/android/accounts/Constants.java
Normal file
48
core/java/android/accounts/Constants.java
Normal file
@@ -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";
|
||||
}
|
||||
46
core/java/android/accounts/Future1.java
Normal file
46
core/java/android/accounts/Future1.java
Normal file
@@ -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<V> extends Future<V> {
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
20
core/java/android/accounts/Future1Callback.java
Normal file
20
core/java/android/accounts/Future1Callback.java
Normal file
@@ -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<V> {
|
||||
void run(Future1<V> future);
|
||||
}
|
||||
57
core/java/android/accounts/Future2.java
Normal file
57
core/java/android/accounts/Future2.java
Normal file
@@ -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<Bundle> {
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
20
core/java/android/accounts/Future2Callback.java
Normal file
20
core/java/android/accounts/Future2Callback.java
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
31
core/java/android/accounts/NetworkErrorException.java
Normal file
31
core/java/android/accounts/NetworkErrorException.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
31
core/java/android/accounts/OperationCanceledException.java
Normal file
31
core/java/android/accounts/OperationCanceledException.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@
|
||||
<string name="RestrictedOnNormal">Voice/SMS service is blocked.</string>
|
||||
<!-- Displayed to tell the user that all voice service is blocked by access control. -->
|
||||
<string name="RestrictedOnAll">All voice/SMS services are blocked.</string>
|
||||
|
||||
|
||||
<!-- Mappings between TS 27.007 +CFCC/+CLCK "service classes" and human-readable strings--> <skip />
|
||||
<!-- Example: Service was enabled for: Voice, Data -->
|
||||
<string name="serviceClassVoice">Voice</string>
|
||||
@@ -206,6 +206,13 @@
|
||||
<!-- Displayed when a request failed because there are too many requests right now. -->
|
||||
<string name="httpErrorTooManyRequests">Too many requests are being processed. Try again later.</string>
|
||||
|
||||
<!-- Account notifications --> <skip />
|
||||
<!-- A notification is shown when the AccountManager is unable to
|
||||
supply an auth token without prompting the user to re-enter the
|
||||
password. This is the text that will scroll through the
|
||||
notification bar (will be seen by the user as he uses another application). -->
|
||||
<string name="notification_title">Sign-in error</string>
|
||||
|
||||
<!-- Sync notifications --> <skip />
|
||||
<!-- A notification is shown when there is a sync error. This is the text that will scroll through the notification bar (will be seen by the user as he uses another application). -->
|
||||
<string name="contentServiceSync">Sync</string>
|
||||
@@ -276,7 +283,7 @@
|
||||
|
||||
<!-- Label for the Android system components when they are shown to the user. -->
|
||||
<string name="android_system_label">Android System</string>
|
||||
|
||||
|
||||
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
<string name="permgrouplab_costMoney">Services that cost you money</string>
|
||||
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
@@ -1292,7 +1299,7 @@
|
||||
|
||||
<!-- Title of the WebView save password dialog. If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. -->
|
||||
<string name="save_password_label">Confirm</string>
|
||||
|
||||
|
||||
<!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Text in the save password dialog, asking if the browser should remember a password. -->
|
||||
<string name="save_password_message">Do you want the browser to remember this password?</string>
|
||||
<!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Button in the save password dialog, saying not to remember this password. -->
|
||||
@@ -1320,8 +1327,8 @@
|
||||
<string name="menu_delete_shortcut_label">delete</string>
|
||||
|
||||
<!-- Strings used for search bar --><skip />
|
||||
|
||||
<!-- This is the default button label in the system-wide search UI.
|
||||
|
||||
<!-- This is the default button label in the system-wide search UI.
|
||||
It is also used by the home screen's search "widget". It should be short -->
|
||||
<string name="search_go">Search</string>
|
||||
|
||||
@@ -1383,7 +1390,7 @@
|
||||
<item quantity="one">tomorrow</item>
|
||||
<item quantity="other">in <xliff:g id="count">%d</xliff:g> days</item>
|
||||
</plurals>
|
||||
|
||||
|
||||
<!-- This is used to express that something occurred some number of abbreviated seconds in the past (e.g., 5 secs ago). -->
|
||||
<plurals name="abbrev_num_seconds_ago">
|
||||
<item quantity="one">1 sec ago</item>
|
||||
@@ -1511,7 +1518,7 @@
|
||||
<!-- Numeric form of the day. Example: "12/31/2007" -->
|
||||
<string name="numeric_date">"<xliff:g id="month" example="12">%m</xliff:g>/<xliff:g id="day" example="31">%d</xliff:g>/<xliff:g id="year" example="2008">%Y</xliff:g>"</string>
|
||||
|
||||
<!-- Format indicating a range of time, from a time on one day to a time on another day.
|
||||
<!-- Format indicating a range of time, from a time on one day to a time on another day.
|
||||
Example: "Mon, Dec 31, 2007, 8am - Tue, Jan 1, 2008, 5pm" -->
|
||||
<string name="wday1_date1_time1_wday2_date2_time2">"<xliff:g id="weekday1" example="Monday">%1$s</xliff:g>, <xliff:g id="date1" example="December 31, 2007">%2$s</xliff:g>, <xliff:g id="time1" example="8am">%3$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Tuesday">%4$s</xliff:g>, <xliff:g id="date2" example="January 1, 2008">%5$s</xliff:g>, <xliff:g id="time2" example="5pm">%6$s</xliff:g>"</string>
|
||||
|
||||
@@ -1519,7 +1526,7 @@
|
||||
Example: "Mon, Dec 31, 2007 - Tue, Jan 1, 2008" -->
|
||||
<string name="wday1_date1_wday2_date2">"<xliff:g id="weekday1" example="Monday">%1$s</xliff:g>, <xliff:g id="date1" example="Dec 31, 2007">%2$s</xliff:g> \u2013 <xliff:g id="weekday2" example="Thursday">%4$s</xliff:g>, <xliff:g id="date2" example="Jan 1, 2008">%5$s</xliff:g>"</string>
|
||||
|
||||
<!-- Format indicating a range of time, from a time on one day to a time on another day.
|
||||
<!-- Format indicating a range of time, from a time on one day to a time on another day.
|
||||
Example: "Dec 31, 2007, 8am - Jan 1, 2008, 5pm" -->
|
||||
<string name="date1_time1_date2_time2">"<xliff:g id="date1" example="Dec 31, 2007">%2$s</xliff:g>, <xliff:g id="time1" example="8am">%3$s</xliff:g> \u2013 <xliff:g id="date2" example="Jan 1, 2008">%5$s</xliff:g>, <xliff:g id="time2" example="5pm">%6$s</xliff:g>"</string>
|
||||
|
||||
@@ -2216,7 +2223,7 @@
|
||||
<string name="usb_storage_button_mount">Mount</string>
|
||||
<!-- See USB_STORAGE. This is the button text to ignore the plugging in of the phone.. -->
|
||||
<string name="usb_storage_button_unmount">Don\'t mount</string>
|
||||
<!-- See USB_STORAGE_DIALOG. If there was an error mounting, this is the text. -->
|
||||
<!-- See USB_STORAGE_DIALOG. If there was an error mounting, this is the text. -->
|
||||
<string name="usb_storage_error_message">There is a problem using your SD card for USB storage.</string>
|
||||
<!-- USB_STORAGE: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across. This is the title -->
|
||||
<string name="usb_storage_notification_title">USB connected</string>
|
||||
@@ -2237,7 +2244,7 @@
|
||||
<string name="usb_storage_stop_button_mount">Turn Off</string>
|
||||
<!-- See USB_STORAGE_STOP. This is the button text to cancel stoping usb storage. -->
|
||||
<string name="usb_storage_stop_button_unmount">Cancel</string>
|
||||
<!-- See USB_STORAGE_STOP_DIALOG. If there was an error stopping, this is the text. -->
|
||||
<!-- See USB_STORAGE_STOP_DIALOG. If there was an error stopping, this is the text. -->
|
||||
<string name="usb_storage_stop_error_message">We've encountered a problem turning off USB storage. Check to make sure you have unmounted the USB host, then try again.</string>
|
||||
|
||||
<!-- External media format dialog strings -->
|
||||
@@ -2258,10 +2265,10 @@
|
||||
|
||||
<!-- Title of the pop-up dialog in which the user switches input method components. -->
|
||||
<string name="select_input_method">Select Input Method</string>
|
||||
|
||||
|
||||
<string name="fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
|
||||
<string name="fast_scroll_numeric_alphabet">\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
|
||||
|
||||
|
||||
<string name="candidates_style"><u>candidates</u></string>
|
||||
|
||||
<!-- External media notification strings -->
|
||||
@@ -2308,23 +2315,23 @@
|
||||
|
||||
<!-- Long label for a button on a full-screen input method for the "Go" action. -->
|
||||
<string name="ime_action_go">Go</string>
|
||||
|
||||
|
||||
<!-- Long label for a button on a full-screen input method for the "Search" action. -->
|
||||
<string name="ime_action_search">Search</string>
|
||||
|
||||
|
||||
<!-- Long label for a button on a full-screen input method for the "Send" action. -->
|
||||
<string name="ime_action_send">Send</string>
|
||||
|
||||
|
||||
<!-- Long label for a button on a full-screen input method for the "Next" action. -->
|
||||
<string name="ime_action_next">Next</string>
|
||||
|
||||
|
||||
<!-- Long label for a button on a full-screen input method for the "Done" action. -->
|
||||
<string name="ime_action_done">Done</string>
|
||||
|
||||
|
||||
<!-- Long label for a button on a full-screen input method for an unknown action. -->
|
||||
<string name="ime_action_default">Execute</string>
|
||||
|
||||
<!-- Strings for search suggestions. These are going here because they are referenced by both
|
||||
|
||||
<!-- Strings for search suggestions. These are going here because they are referenced by both
|
||||
ContactsProvider and GoogleContactsProvider -->
|
||||
<skip />
|
||||
|
||||
@@ -2337,7 +2344,7 @@
|
||||
|
||||
<!-- This string array should be overridden by the manufacture to present a list of carrier-id,locale pairs. This is used at startup to set a default locale by checking the system property ro.carrier for the carrier-id and searching through this array -->
|
||||
<string-array translatable="false" name="carrier_locales">
|
||||
</string-array>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user