Android系统中可以使用AccountManager服务进行帐号的管理(添加,删除,以及其他属性的设置和访问),本文档主要讨论调用remove删除帐号的情况.AccountManager系统服务中有2个接口可以删除帐号,分别是

public boolean removeAccountExplicitly(Account account)

public AccountManagerFuture<Bundle> removeAccount(final Account account,final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler)

这两个接口的区别是:

  • removeAccountExplicitly是一个同步调用,将直接从AccountSQLite数据库中删除该帐号.
    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<Bundle> removeAccount(final Account account,final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {if (account == null) throw new IllegalArgumentException("account is null");return new AmsTask(activity, handler, callback) {@Overridepublic void doWork() throws RemoteException {mService.removeAccount(mResponse, account, activity != null);}}.start();}

该函数创建了一个匿名的AmsTask类对象,然后调用其start方法.其中类AmsTask的实现为(AccountManager.java):

private abstract class AmsTask extends FutureTask<Bundle> implements AccountManagerFuture<Bundle> {final IAccountManagerResponse mResponse;final Handler mHandler;final AccountManagerCallback<Bundle> mCallback;final Activity mActivity;public AmsTask(Activity activity, Handler handler, AccountManagerCallback<Bundle> callback) {super(new Callable<Bundle>() {@Overridepublic Bundle call() throws Exception {throw new IllegalStateException("this should never be called");}});mHandler = handler;mCallback = callback;mActivity = activity;mResponse = new Response();}public final AccountManagerFuture<Bundle> start() {try {doWork();} catch (RemoteException e) {setException(e);}return this;}@Overrideprotected 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();}@Overridepublic Bundle getResult()throws OperationCanceledException, IOException, AuthenticatorException {return internalGetResult(null, null);}@Overridepublic Bundle getResult(long timeout, TimeUnit unit)throws OperationCanceledException, IOException, AuthenticatorException {return internalGetResult(timeout, unit);}@Overrideprotected void done() {if (mCallback != null) {postToHandler(mHandler, mCallback, this);}}/** Handles the responses from the AccountManager */private class Response extends IAccountManagerResponse.Stub {@Overridepublic 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 seemActivity.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);}}@Overridepublic 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 nowcancel(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 usersif (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<Pair<Account, String>, 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;}@Overrideprotected String toDebugString(long now) {return super.toDebugString(now) + ", removeAccount"+ ", account " + mAccount;}@Overridepublic void run() throws RemoteException {mAuthenticator.getAccountRemovalAllowed(this, mAccount);}@Overridepublic 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.Stubimplements 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 closedreturn null;}IAccountManagerResponse response = mResponse;close(); // this clears mResponse so we need to save the response before this callreturn 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 nowreturn;}}if (mResponse != null) {// stop listening for response deathsmResponse.asBinder().unlinkToDeath(this, 0 /* flags */);// clear this so that we don't accidentally send any further resultsmResponse = null;}cancelTimeout();unbind();}@Overridepublic 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);}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);try {run();} catch (RemoteException e) {onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,"remote exception");}}@Overridepublic 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);}}}}@Overridepublic 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 successfulboolean 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 0response.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 exceptionsif (Log.isLoggable(TAG, Log.VERBOSE)) {Log.v(TAG, "failure while notifying response", e);}}}}@Overridepublic void onRequestContinued() {mNumRequestContinued++;}@Overridepublic 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<AuthenticatorDescription> 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中有如下类似定义:

<serviceandroid:name="com.myaccount.demo.MyAuthenticatorService"android:exported="true"><intent-filter><action android:name="android.accounts.AccountAuthenticator"/></intent-filter><meta-dataandroid:name="android.accounts.AccountAuthenticator"android:resource="@xml/authenticator" />
</service>

其中intent-filter中指定的action即为AccountManager.ACTION_AUTHENTICATOR_INTENT的值,meta-data中指定的文件中定义帐号的属性.该自定义服务的的一个简单但完备的实现如下:

public class MyAuthenticatorService extends Service {private MyAuthenticator mAuthenticator;@Overridepublic void onCreate() {mAuthenticator = new MyAuthenticator(this);}@Overridepublic IBinder onBind(Intent intent) {return mAuthenticator.getIBinder();}
}

自定义类MyAuthenticatorService的简单实现为:

public class MyAuthenticator extends AbstractAccountAuthenticator {public SyncAuthenticator(Context context) {super(context);}// Some other override methods@Overridepublic 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    @Overridepublic 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());
}

RemoveAccountSessiononResult方法在真正删除帐号后, 调用父类对应的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);}
}

mHandlermCallback是我们最开始调用removeAccount传入的参数,所以将在mHandler所在的线程回调mCallback, 将获得帐号删除的操作结果.

至此,AccountManager系统服务调用removeAccount删除帐号的总体流程结束.

关于使用AccountManager的remove删除Android帐号的细节相关推荐

  1. 查看删除的帐号的DN名称(活动目录快照配置管理系列六)

    1. 单击"开始"菜单,在搜索框中输入"ldp": 2. 在ldp窗口中,单击"连接",在下图所示页面中,输要连接的快照,本例中我们只对DC ...

  2. mysql 删除多余帐号_安装完mysql数据库后的优化(删除多余用户和数据库)

    安装完mysql数据库后的优化(删除多余用户和数据库) 发布时间:2020-06-27 19:09:35 来源:51CTO 阅读:2761 作者:冰冻vs西瓜 栏目:数据库 1.查看数据库的版本信息: ...

  3. php ftp 账号密码修改,月光软件站 - 编程文档 - 其他语言 - 用PHP即时添加,删除FTP帐号和更改FTP密码的函数...

    网上不知道有没有Serv-U FTP的接口,这类的资料,由于不知道Serv-U的加密方式,所以没有使用加密密码的方式,如果有人知道算法,麻烦你告诉我一声 $filepath是Serv-U的安装地址,$ ...

  4. android 帐号密码xml,《Android移动应用基础教程》之保存QQ账号和密码

    FileSaveQQ.java package njitt.software.saveqq; import android.content.Context; import java.io.FileIn ...

  5. Android AccountManager帐号管理(二)

    Android AccountManager 帐号管理(一)分享了如何将自己的帐号体系注册到系统服务AccountManagerService,统一由AccountManager来管理,这仅是自己的一 ...

  6. Android AccountManager帐号管理(一)

    AccountManager简介 AccountManager帐号管理器,集中管理apps注册的不同类型的帐号. 不同类型的帐号服务会使用不同的帐号登录和鉴权方式,所以AccountManager为不 ...

  7. 计算机用户域怎么删除,如何查找并删除AD域中多余的计算机帐号?

    如何将过期的计算机从computers容器中自动清除?因为有很多时候没有按正常的方法退域,导致在AD的computers容器中存在很多过时的计算机,有没有办法让它自动清除呢 对我们广大网络管理员来说, ...

  8. xp删除管理员账户_在Windows XP中从登录屏幕删除用户帐户

    xp删除管理员账户 So you login to your computer every single day, but there's more than one account to choos ...

  9. 在AD中批量添加多个用户帐号

    问题: 如何批量的创建帐号? 解决方案: 在利用CSVDE.EXE或LDIFDE.EXE来批量创建用户帐号 实验环境: Windows 2003 如果我们要想批量的创建帐号的话,我们可以首先利用文字编 ...

最新文章

  1. [BZOJ2761/Luogu4305][JLOI2011]不重复数字 题解
  2. mysql amp;amp;_浅析mysql交互式连接amp;非交互式连接
  3. linux系统上不去网,linux 上不去网
  4. python -m以模块方式启动,python命令加上-u(unbuffered)参数后会强制其标准输出
  5. [Verilog] 薄膜建盤4X4 電路程式設計
  6. java trimprefix_MyBatis动态SQL中的trim标签的使用方法
  7. Mysql内连接和外连接的区别
  8. HotSpot虚拟机的垃圾收集算法实现
  9. Mac Quicktime 录屏带声音
  10. java 两个url对比_一个URL模式中的两个slu ..
  11. Python之父愤然退位:再也无法忍受他们鄙视我的意见
  12. Cisco 2960 3750交换机端口流量限速(QOS)
  13. 插入栈顶元素c语言,详解数据结构之顺序栈的基本操做(C语言描述)
  14. VBA代码为ppt添加页码进度条
  15. Compose 页面切换动画
  16. 【洛谷 P1710】地铁涨价(dfs+bfs)
  17. QDUOJ 点击此处签到(并查集)
  18. 云栖大会人脸识别闸机【技术亮点篇7】--人脸识别闸机采用
  19. 如何安装wmware tools
  20. 阿里云镜像站NTP——Windows11操作系统配置方法

热门文章

  1. 一台服务器 运行虚拟机吗,(原创)关于一台服务器能安装多少台虚拟机问题?...
  2. UE4 Pak文件操作
  3. 【Hard to Park】Estimating Parking Difficulty at Scale
  4. 计算机器人正反运动学方程,机器人运动学方程
  5. 安装RocketChat报错:npm WARN saveError ENOENT: no such file or directory, open ‘/tmp/bundle/programs/web.
  6. 学习Excel一定要精通VBA才是高手吗?
  7. 【Unity3D】人机交互Input
  8. 使用VGG-19模型训练自己的数据集
  9. python黑屏改成白底_详解Python给照片换底色(蓝底换红底)
  10. 服务过美国总统竞选的非传统投票UI【demo已放出】