关于使用AccountManager的remove删除Android帐号的细节
Android系統(tǒng)中可以使用AccountManager服務(wù)進行帳號的管理(添加,刪除,以及其他屬性的設(shè)置和訪問),本文檔主要討論調(diào)用remove刪除帳號的情況.AccountManager系統(tǒng)服務(wù)中有2個接口可以刪除帳號,分別是
public boolean removeAccountExplicitly(Account account)和
public AccountManagerFuture<Bundle> removeAccount(final Account account,final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler)這兩個接口的區(qū)別是:
- removeAccountExplicitly是一個同步調(diào)用,將直接從Account的SQLite數(shù)據(jù)庫中刪除該帳號.
removeAccount是一個異步調(diào)用. 該函數(shù)有2種使用方式獲得操作結(jié)果,第一種方式是使用輸入?yún)?shù)指定的回調(diào)callback,通過設(shè)置輸入?yún)?shù)handler, 可以指定該回調(diào)函數(shù)的執(zhí)行線程, 如果handler設(shè)置為null, 則在主線程執(zhí)行該回調(diào). 如果回調(diào)函數(shù)輸入為null, 刪除操作完成時,不執(zhí)行回調(diào)操作.第二種獲得操作結(jié)果的方式是通過返回值,因為removeAccount內(nèi)部是異步操作,所以該函數(shù)的返回值使用了Future模式,調(diào)用其get方法獲得刪除操作結(jié)果,如果刪除操作沒有完成,get方法將阻塞. - removeAccountExplicitly刪除帳號時,不會檢查自定義的Authenticator類中的getAccountRemovalAllowed方法是否允許刪除該帳號.
removeAccount則相反, 會做這個檢查. 因此,在我們自己實現(xiàn)的Authenticator類中, 可以覆寫getAccountRemovalAllowed方法,就可以決定removeAccount是否可以刪除該帳號.
需要注意的是,在手機的設(shè)置里面手動刪除帳號時,也會檢查帳號關(guān)聯(lián)的自定義Authenticator類的getAccountRemovalAllowed方法,如果該方法不允許刪除該帳號,則手動刪除失敗.
下面主要討論removeAccount的一些實現(xiàn)細節(jié).該函數(shù)的實現(xiàn)為(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();}該函數(shù)創(chuàng)建了一個匿名的AmsTask類對象,然后調(diào)用其start方法.其中類AmsTask的實現(xiàn)為(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方法將調(diào)用doWork, 創(chuàng)建的AmsTask對象覆寫了該方法, 也就是執(zhí)行mService.removeAccount(mResponse, account, activity != null); 將執(zhí)行AccountManagerService中對應(yīng)的函數(shù)(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);} }在函數(shù)removeAccountAsUser中,最后調(diào)用new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind(); 即創(chuàng)建了一個RemoveAccountSession對象,然后調(diào)用其bind方法, 該類及其父類Session的實現(xiàn)為(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方法實現(xiàn)在其父類Session中,進一步調(diào)用bindToAuthenticator, 在還函數(shù)中, 創(chuàng)建一個Intent對象, 其action設(shè)置為AccountManager.ACTION_AUTHENTICATOR_INTENT, component設(shè)置為自定義authenticator所在的服務(wù).接著調(diào)用函數(shù)bindServiceAsUser, 傳入的ServiceConnection回調(diào)為this, 即RemoveAccountSession對象本身, 這是因為類RemoveAccountSession的父類Session執(zhí)行了接口ServiceConnection.
被bind的自定義服務(wù)需要在AndroidManifest.xml中有如下類似定義:
其中intent-filter中指定的action即為AccountManager.ACTION_AUTHENTICATOR_INTENT的值,meta-data中指定的文件中定義帳號的屬性.該自定義服務(wù)的的一個簡單但完備的實現(xiàn)如下:
public class MyAuthenticatorService extends Service {private MyAuthenticator mAuthenticator;@Overridepublic void onCreate() {mAuthenticator = new MyAuthenticator(this);}@Overridepublic IBinder onBind(Intent intent) {return mAuthenticator.getIBinder();} }自定義類MyAuthenticatorService的簡單實現(xiàn)為:
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,設(shè)置為false,則帳號不能被刪除,如果設(shè)置為true,則帳號可以刪除.自定義服務(wù)MyAuthenticatorService 中執(zhí)行的mAuthenticator.getIBinder()返回的是一個IBinder對象, 該方法實現(xiàn)在父類AbstractAccountAuthenticator中(AbstractAccountAuthenticator.java):
public final IBinder getIBinder() {return mTransport.asBinder(); }而mTransport是父類AbstractAccountAuthenticator中一個私有成員變量: private Transport mTransport = new Transport(); 類Transport是類AbstractAccountAuthenticator中的一個內(nèi)部類, 其定義為(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對象, 調(diào)用該對象的getAccountRemovalAllowed方法時, 首先調(diào)用自定義類MyAuthenticator中覆寫的該方法,然后調(diào)用response.onResult(result);
函數(shù)bindServiceAsUser在bind到我們的自定義服務(wù)后,onServiceConnected回調(diào)被執(zhí)行,mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);將返回類Transport對象,接著調(diào)用類RemoveAccountSession中的run方法,即調(diào)用mAuthenticator.getAccountRemovalAllowed(this, mAccount);也就是調(diào)用Transport類中的getAccountRemovalAllowed方法,其第一個輸入?yún)?shù)為this, 即RemoveAccountSession對象,這是因為其父類Session執(zhí)行了IAccountAuthenticatorResponse.Stub接口, 因此當在Transport類的方法getAccountRemovalAllowed中執(zhí)行response.onResult(result);時, 將回調(diào)服務(wù)端(RemoveAccountSession)對象的對應(yīng)方法onResult,該方法會檢查自定義的MyAuthenticator是否允許刪除帳號的返回結(jié)果,如果允許,則調(diào)用真正的帳號刪除函數(shù)removeAccountInternal.
if (removalAllowed) {removeAccountInternal(mAccounts, mAccount, getCallingUid()); }類RemoveAccountSession的onResult方法在真正刪除帳號后, 調(diào)用父類對應(yīng)的onResult方法,該方法中,使用IAccountManagerResponse response;對象的onResult方法回調(diào)通知類AmsTask對象, 因為AmsTask對象發(fā)起帳號刪除操作的函數(shù)doWork中, 傳入的輸入?yún)?shù)mResponse是其內(nèi)部類Response對象.
public void doWork() throws RemoteException {mService.removeAccount(mResponse, account, false); }AmsTask的內(nèi)部類Response是一個IAccountManagerResponse接口的服務(wù)端:
private class Response extends IAccountManagerResponse.Stub其函數(shù) onResult中將繼續(xù)調(diào)用set(bundle);其中調(diào)用父類FutureTask中的set方法,finishCompletion方法和done方法,類AmsTask覆寫其父類FutureTask中的done方法:
@Override protected void done() {if (mCallback != null) {postToHandler(mHandler, mCallback, this);} }mHandler和mCallback是我們最開始調(diào)用removeAccount傳入的參數(shù),所以將在mHandler所在的線程回調(diào)mCallback, 將獲得帳號刪除的操作結(jié)果.
至此,AccountManager系統(tǒng)服務(wù)調(diào)用removeAccount刪除帳號的總體流程結(jié)束.
總結(jié)
以上是生活随笔為你收集整理的关于使用AccountManager的remove删除Android帐号的细节的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [buuctf] crypto全解——前
- 下一篇: android sina oauth2.