Android系统中可以使用AccountManager
服务进行帐号的管理(添加,删除,以及其他属性的设置和访问),本文档主要讨论调用remove删除帐号的情况.AccountManager系统服务中有2个接口可以删除帐号,分别是
public boolean removeAccountExplicitly(Account account)
和
public AccountManagerFuture removeAccount(final Account account,
final Activity activity, AccountManagerCallback callback, Handler handler)
这两个接口的区别是:
removeAccountExplicitly
是一个同步调用,将直接从Account
的SQLite
数据库中删除该帐号.removeAccount
是一个异步调用. 该函数有2种使用方式获得操作结果,第一种方式是使用输入参数指定的回调callback
,通过设置输入参数handler
, 可以指定该回调函数的执行线程, 如果handler设置为null, 则在主线程执行该回调. 如果回调函数输入为null, 删除操作完成时,不执行回调操作.第二种获得操作结果的方式是通过返回值,因为removeAccount内部是异步操作,所以该函数的返回值使用了Future
模式,调用其get
方法获得删除操作结果,如果删除操作没有完成,get方法将阻塞.removeAccountExplicitly
删除帐号时,不会检查自定义的Authenticator
类中的getAccountRemovalAllowed
方法是否允许删除该帐号.removeAccount
则相反, 会做这个检查. 因此,在我们自己实现的Authenticator类中, 可以覆写getAccountRemovalAllowed方法,就可以决定removeAccount是否可以删除该帐号.需要注意的是,在手机的设置里面手动删除帐号时,也会检查帐号关联的自定义Authenticator类的getAccountRemovalAllowed方法,如果该方法不允许删除该帐号,则手动删除失败.
下面主要讨论removeAccount
的一些实现细节.该函数的实现为(AccountManager.java):
public AccountManagerFuture removeAccount(final Account account,
final Activity activity, AccountManagerCallback callback, Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null");
return new AmsTask(activity, handler, callback) {
@Override
public void doWork() throws RemoteException {
mService.removeAccount(mResponse, account, activity != null);
}
}.start();
}
该函数创建了一个匿名的AmsTask
类对象,然后调用其start
方法.其中类AmsTask的实现为(AccountManager.java):
private abstract class AmsTask extends FutureTask implements AccountManagerFuture {
final IAccountManagerResponse mResponse;
final Handler mHandler;
final AccountManagerCallback mCallback;
final Activity mActivity;
public AmsTask(Activity activity, Handler handler, AccountManagerCallback callback) {
super(new Callable() {
@Override
public Bundle call() throws Exception {
throw new IllegalStateException("this should never be called");
}
});
mHandler = handler;
mCallback = callback;
mActivity = activity;
mResponse = new Response();
}
public final AccountManagerFuture start() {
try {
doWork();
} catch (RemoteException e) {
setException(e);
}
return this;
}
@Override
protected void set(Bundle bundle) {
// TODO: somehow a null is being set as the result of the Future. Log this
// case to help debug where this is occurring. When this bug is fixed this
// condition statement should be removed.
if (bundle == null) {
Log.e(TAG, "the bundle must not be null", new Exception());
}
super.set(bundle);
}
public abstract void doWork() throws RemoteException;
private Bundle internalGetResult(Long timeout, TimeUnit unit)
throws OperationCanceledException, IOException, AuthenticatorException {
if (!isDone()) {
ensureNotOnMainThread();
}
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();
}
@Override
public Bundle getResult()
throws OperationCanceledException, IOException, AuthenticatorException {
return internalGetResult(null, null);
}
@Override
public Bundle getResult(long timeout, TimeUnit unit)
throws OperationCanceledException, IOException, AuthenticatorException {
return internalGetResult(timeout, unit);
}
@Override
protected void done() {
if (mCallback != null) {
postToHandler(mHandler, mCallback, this);
}
}
/** Handles the responses from the AccountManager */
private class Response extends IAccountManagerResponse.Stub {
@Override
public void onResult(Bundle bundle) {
Intent intent = bundle.getParcelable(KEY_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 if (bundle.getBoolean("retry")) {
try {
doWork();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
set(bundle);
}
}
@Override
public void onError(int code, String message) {
if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED
|| code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
// the authenticator indicated that this request was canceled or we were
// forbidden to fulfill; cancel now
cancel(true /* mayInterruptIfRunning */);
return;
}
setException(convertErrorToException(code, message));
}
}
}
其start
方法将调用doWork
, 创建的AmsTask对象覆写了该方法, 也就是执行mService.removeAccount(mResponse, account, activity != null);
将执行AccountManagerService
中对应的函数(AccountManagerService.java):
@Override
public void removeAccount(IAccountManagerResponse response, Account account,
boolean expectActivityLaunch) {
removeAccountAsUser(
response,
account,
expectActivityLaunch,
UserHandle.getCallingUserId());
}
@Override
public void removeAccountAsUser(IAccountManagerResponse response, Account account,
boolean expectActivityLaunch, int userId) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "removeAccount: " + account
+ ", response " + response
+ ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid()
+ ", for user id " + userId);
}
Preconditions.checkArgument(account != null, "account cannot be null");
Preconditions.checkArgument(response != null, "response cannot be null");
// Only allow the system process to modify accounts of other users
if (isCrossUser(callingUid, userId)) {
throw new SecurityException(
String.format(
"User %s tying remove account for %s" ,
UserHandle.getCallingUserId(),
userId));
}
/*
* Only the system or authenticator should be allowed to remove accounts for that
* authenticator. This will let users remove accounts (via Settings in the system) but not
* arbitrary applications (like competing authenticators).
*/
UserHandle user = UserHandle.of(userId);
if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
&& !isSystemUid(callingUid)) {
String msg = String.format(
"uid %s cannot remove accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
if (!canUserModifyAccounts(userId, callingUid)) {
try {
response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
"User cannot modify accounts");
} catch (RemoteException re) {
}
return;
}
if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
try {
response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
"User cannot modify accounts of this type (policy).");
} catch (RemoteException re) {
}
return;
}
long identityToken = clearCallingIdentity();
UserAccounts accounts = getUserAccounts(userId);
cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
synchronized(accounts.credentialsPermissionNotificationIds) {
for (Pair, Integer> pair:
accounts.credentialsPermissionNotificationIds.keySet()) {
if (account.equals(pair.first.first)) {
NotificationId id = accounts.credentialsPermissionNotificationIds.get(pair);
cancelNotification(id, user);
}
}
}
final long accountId = accounts.accountsDb.findDeAccountId(account);
logRecord(
AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
AccountsDb.TABLE_ACCOUNTS,
accountId,
accounts,
callingUid);
try {
new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
} finally {
restoreCallingIdentity(identityToken);
}
}
在函数removeAccountAsUser
中,最后调用new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
即创建了一个RemoveAccountSession
对象,然后调用其bind
方法, 该类及其父类Session
的实现为(AccountManagerService.java):
private class RemoveAccountSession extends Session {
final Account mAccount;
public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Account account, boolean expectActivityLaunch) {
super(accounts, response, account.type, expectActivityLaunch,
true /* stripAuthTokenFromResult */, account.name,
false /* authDetailsRequired */);
mAccount = account;
}
@Override
protected String toDebugString(long now) {
return super.toDebugString(now) + ", removeAccount"
+ ", account " + mAccount;
}
@Override
public void run() throws RemoteException {
mAuthenticator.getAccountRemovalAllowed(this, mAccount);
}
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
&& !result.containsKey(AccountManager.KEY_INTENT)) {
final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
if (removalAllowed) {
removeAccountInternal(mAccounts, mAccount, getCallingUid());
}
IAccountManagerResponse response = getResponseAndClose();
if (response != null) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
+ response);
}
Bundle result2 = new Bundle();
result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
try {
response.onResult(result2);
} catch (RemoteException e) {
// ignore
}
}
}
super.onResult(result);
}
}
private abstract class Session extends IAccountAuthenticatorResponse.Stub
implements IBinder.DeathRecipient, ServiceConnection {
IAccountManagerResponse mResponse;
final String mAccountType;
final boolean mExpectActivityLaunch;
final long mCreationTime;
final String mAccountName;
// Indicates if we need to add auth details(like last credential time)
final boolean mAuthDetailsRequired;
// If set, we need to update the last authenticated time. This is
// currently
// used on
// successful confirming credentials.
final boolean mUpdateLastAuthenticatedTime;
public int mNumResults = 0;
private int mNumRequestContinued = 0;
private int mNumErrors = 0;
IAccountAuthenticator mAuthenticator = null;
private final boolean mStripAuthTokenFromResult;
protected final UserAccounts mAccounts;
public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
boolean authDetailsRequired) {
this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
}
public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
super();
//if (response == null) throw new IllegalArgumentException("response is null");
if (accountType == null) throw new IllegalArgumentException("accountType is null");
mAccounts = accounts;
mStripAuthTokenFromResult = stripAuthTokenFromResult;
mResponse = response;
mAccountType = accountType;
mExpectActivityLaunch = expectActivityLaunch;
mCreationTime = SystemClock.elapsedRealtime();
mAccountName = accountName;
mAuthDetailsRequired = authDetailsRequired;
mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
synchronized (mSessions) {
mSessions.put(toString(), this);
}
if (response != null) {
try {
response.asBinder().linkToDeath(this, 0 /* flags */);
} catch (RemoteException e) {
mResponse = null;
binderDied();
}
}
}
IAccountManagerResponse getResponseAndClose() {
if (mResponse == null) {
// this session has already been closed
return null;
}
IAccountManagerResponse response = mResponse;
close(); // this clears mResponse so we need to save the response before this call
return response;
}
/**
* Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
* security policy.
*
* In particular we want to make sure that the Authenticator doesn't try to trick users
* into launching arbitrary intents on the device via by tricking to click authenticator
* supplied entries in the system Settings app.
*/
protected void checkKeyIntent(
int authUid,
Intent intent) throws SecurityException {
intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION));
long bid = Binder.clearCallingIdentity();
try {
PackageManager pm = mContext.getPackageManager();
ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
int targetUid = targetActivityInfo.applicationInfo.uid;
if (!isExportedSystemActivity(targetActivityInfo)
&& (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
targetUid))) {
String pkgName = targetActivityInfo.packageName;
String activityName = targetActivityInfo.name;
String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
+ "does not share a signature with the supplying authenticator (%s).";
throw new SecurityException(
String.format(tmpl, activityName, pkgName, mAccountType));
}
} finally {
Binder.restoreCallingIdentity(bid);
}
}
private boolean isExportedSystemActivity(ActivityInfo activityInfo) {
String className = activityInfo.name;
return "android".equals(activityInfo.packageName) &&
(GrantCredentialsPermissionActivity.class.getName().equals(className)
|| CantAddAccountActivity.class.getName().equals(className));
}
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();
}
@Override
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 (!bindToAuthenticator(mAccountType)) {
Log.d(TAG, "bind attempt failed for " + toDebugString());
onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
}
}
private void unbind() {
if (mAuthenticator != null) {
mAuthenticator = null;
mContext.unbindService(this);
}
}
public void cancelTimeout() {
mHandler.removeMessages(MESSAGE_TIMED_OUT, this);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
try {
run();
} catch (RemoteException e) {
onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
"remote exception");
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mAuthenticator = null;
IAccountManagerResponse response = getResponseAndClose();
if (response != null) {
try {
response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
"disconnected");
} catch (RemoteException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Session.onServiceDisconnected: "
+ "caught RemoteException while responding", e);
}
}
}
}
public abstract void run() throws RemoteException;
public void onTimedOut() {
IAccountManagerResponse response = getResponseAndClose();
if (response != null) {
try {
response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
"timeout");
} catch (RemoteException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
e);
}
}
}
}
@Override
public void onResult(Bundle result) {
Bundle.setDefusable(result, true);
mNumResults++;
Intent intent = null;
if (result != null) {
boolean isSuccessfulConfirmCreds = result.getBoolean(
AccountManager.KEY_BOOLEAN_RESULT, false);
boolean isSuccessfulUpdateCredsOrAddAccount =
result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
&& result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
// We should only update lastAuthenticated time, if
// mUpdateLastAuthenticatedTime is true and the confirmRequest
// or updateRequest was successful
boolean needUpdate = mUpdateLastAuthenticatedTime
&& (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
if (needUpdate || mAuthDetailsRequired) {
boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
if (needUpdate && accountPresent) {
updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
}
if (mAuthDetailsRequired) {
long lastAuthenticatedTime = -1;
if (accountPresent) {
lastAuthenticatedTime = mAccounts.accountsDb
.findAccountLastAuthenticatedTime(
new Account(mAccountName, mAccountType));
}
result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
lastAuthenticatedTime);
}
}
}
if (result != null
&& (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
checkKeyIntent(
Binder.getCallingUid(),
intent);
}
if (result != null
&& !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
Account account = new Account(accountName, accountType);
cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
new UserHandle(mAccounts.userId));
}
}
IAccountManagerResponse response;
if (mExpectActivityLaunch && result != null
&& result.containsKey(AccountManager.KEY_INTENT)) {
response = mResponse;
} else {
response = getResponseAndClose();
}
if (response != null) {
try {
if (result == null) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, getClass().getSimpleName()
+ " calling onError() on response " + response);
}
response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
"null bundle returned");
} else {
if (mStripAuthTokenFromResult) {
result.remove(AccountManager.KEY_AUTHTOKEN);
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, getClass().getSimpleName()
+ " calling onResult() on response " + response);
}
if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
(intent == null)) {
// All AccountManager error codes are greater than 0
response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
result.getString(AccountManager.KEY_ERROR_MESSAGE));
} else {
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);
}
}
}
}
@Override
public void onRequestContinued() {
mNumRequestContinued++;
}
@Override
public void onError(int errorCode, String errorMessage) {
mNumErrors++;
IAccountManagerResponse response = getResponseAndClose();
if (response != null) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, getClass().getSimpleName()
+ " calling onError() on response " + response);
}
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");
}
}
}
/**
* find the component name for the authenticator and initiate a bind
* if no authenticator or the bind fails then return false, otherwise return true
*/
private boolean bindToAuthenticator(String authenticatorType) {
final AccountAuthenticatorCache.ServiceInfo authenticatorInfo;
authenticatorInfo = mAuthenticatorCache.getServiceInfo(
AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
if (authenticatorInfo == null) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "there is no authenticator for " + authenticatorType
+ ", bailing out");
}
return false;
}
if (!isLocalUnlockedUser(mAccounts.userId)
&& !authenticatorInfo.componentInfo.directBootAware) {
Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
+ " which isn't encryption aware");
return false;
}
Intent intent = new Intent();
intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
intent.setComponent(authenticatorInfo.componentName);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
}
if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
UserHandle.of(mAccounts.userId))) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
}
return false;
}
return true;
}
}
类RemoveAccountSession对象的bind方法实现在其父类Session中,进一步调用bindToAuthenticator
, 在还函数中, 创建一个Intent
对象, 其action设置为AccountManager.ACTION_AUTHENTICATOR_INTENT
, component设置为自定义authenticator所在的服务.接着调用函数bindServiceAsUser
, 传入的ServiceConnection
回调为this
, 即RemoveAccountSession对象本身, 这是因为类RemoveAccountSession的父类Session执行了接口ServiceConnection
.
被bind的自定义服务需要在AndroidManifest.xml
中有如下类似定义:
其中intent-filter
中指定的action即为AccountManager.ACTION_AUTHENTICATOR_INTENT的值,meta-data
中指定的文件中定义帐号的属性.该自定义服务的的一个简单但完备的实现如下:
public class MyAuthenticatorService extends Service {
private MyAuthenticator mAuthenticator;
@Override
public void onCreate() {
mAuthenticator = new MyAuthenticator(this);
}
@Override
public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
}
自定义类MyAuthenticatorService
的简单实现为:
public class MyAuthenticator extends AbstractAccountAuthenticator {
public SyncAuthenticator(Context context) {
super(context);
}
// Some other override methods
@Override
public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response,
Account account) throws NetworkErrorException {
final Bundle result = new Bundle();
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
return result;
}
}
该自定义类MyAuthenticatorService可以覆写父类AbstractAccountAuthenticator
的抽象方法.这里控制帐号能否被removeAccount删除的是getAccountRemovalAllowed
方法, 如果将该方法中的Bundle
对象中的字段AccountManager.KEY_BOOLEAN_RESULT
,设置为false,则帐号不能被删除,如果设置为true,则帐号可以删除.自定义服务MyAuthenticatorService 中执行的mAuthenticator.getIBinder()
返回的是一个IBinder
对象, 该方法实现在父类AbstractAccountAuthenticator中(AbstractAccountAuthenticator.java):
public final IBinder getIBinder() {
return mTransport.asBinder();
}
而mTransport
是父类AbstractAccountAuthenticator中一个私有成员变量: private Transport mTransport = new Transport();
类Transport
是类AbstractAccountAuthenticator中的一个内部类, 其定义为(AbstractAccountAuthenticator.java):
private class Transport extends IAccountAuthenticator.Stub {
// Implementation of some other methods in IAccountAuthenticator
@Override
public void getAccountRemovalAllowed(IAccountAuthenticatorResponse response,
Account account) throws RemoteException {
checkBinderPermission();
try {
final Bundle result = AbstractAccountAuthenticator.this.getAccountRemovalAllowed(
new AccountAuthenticatorResponse(response), account);
if (result != null) {
response.onResult(result);
}
} catch (Exception e) {
handleException(response, "getAccountRemovalAllowed", account.toString(), e);
}
}
}
所以类Transport的对象是个IBinder对象, 调用该对象的getAccountRemovalAllowed方法时, 首先调用自定义类MyAuthenticator中覆写的该方法,然后调用response.onResult(result);
函数bindServiceAsUser在bind到我们的自定义服务后,onServiceConnected
回调被执行,mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
将返回类Transport
对象,接着调用类RemoveAccountSession
中的run
方法,即调用mAuthenticator.getAccountRemovalAllowed(this, mAccount);
也就是调用Transport
类中的getAccountRemovalAllowed
方法,其第一个输入参数为this
, 即RemoveAccountSession对象,这是因为其父类Session
执行了IAccountAuthenticatorResponse.Stub
接口, 因此当在Transport类的方法getAccountRemovalAllowed中执行response.onResult(result);
时, 将回调服务端(RemoveAccountSession)对象的对应方法onResult
,该方法会检查自定义的MyAuthenticator
是否允许删除帐号的返回结果,如果允许,则调用真正的帐号删除函数removeAccountInternal
.
if (removalAllowed) {
removeAccountInternal(mAccounts, mAccount, getCallingUid());
}
类RemoveAccountSession
的onResult
方法在真正删除帐号后, 调用父类对应的onResult
方法,该方法中,使用IAccountManagerResponse response;
对象的onResult
方法回调通知类AmsTask
对象, 因为AmsTask对象发起帐号删除操作的函数doWork
中, 传入的输入参数mResponse
是其内部类Response
对象.
public void doWork() throws RemoteException {
mService.removeAccount(mResponse, account, false);
}
AmsTask的内部类Response是一个IAccountManagerResponse
接口的服务端:
private class Response extends IAccountManagerResponse.Stub
其函数 onResult
中将继续调用set(bundle);
其中调用父类FutureTask中的set
方法,finishCompletion
方法和done
方法,类AmsTask覆写其父类FutureTask中的done
方法:
@Override
protected void done() {
if (mCallback != null) {
postToHandler(mHandler, mCallback, this);
}
}
mHandler
和mCallback
是我们最开始调用removeAccount传入的参数,所以将在mHandler所在的线程回调mCallback, 将获得帐号删除的操作结果.
至此,AccountManager系统服务调用removeAccount删除帐号的总体流程结束.