关于使用AccountManager的remove删除Android帐号的细节
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):
@Overridepublic void removeAccount(IAccountManagerResponse response, Account account, boolean expectActivityLaunch) { removeAccountAsUser( response, account, expectActivityLaunch, UserHandle.getCallingUserId());}@Overridepublic 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 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
方法:
@Overrideprotected void done() { if (mCallback != null) { postToHandler(mHandler, mCallback, this); }}
mHandler
和mCallback
是我们最开始调用removeAccount传入的参数,所以将在mHandler所在的线程回调mCallback, 将获得帐号删除的操作结果.
至此,AccountManager系统服务调用removeAccount删除帐号的总体流程结束.
更多相关文章
- Android入门之addWindow
- 2011.07.12(3)——— android ui的一些概念
- Android(安卓)WebView属性及用法
- 关于android全屏截图,无需root,无状态栏,2个方法
- 通过EventBus更换android app主题
- Android(安卓)闹钟 开发过程记录(二)
- 编译源码生成的SDK,创建android project 没有proguard.cfg问题
- 导入android源码有错,R.java文件不能自动生成解决方法[转]
- android镜像制作方法