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

public boolean removeAccountExplicitly(Account account)

public AccountManagerFuture removeAccount(final Account account,            final Activity activity, AccountManagerCallback 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 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());}

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方法:

@Overrideprotected void done() {    if (mCallback != null) {        postToHandler(mHandler, mCallback, this);    }}

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

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

更多相关文章

  1. Android入门之addWindow
  2. 2011.07.12(3)——— android ui的一些概念
  3. Android(安卓)WebView属性及用法
  4. 关于android全屏截图,无需root,无状态栏,2个方法
  5. 通过EventBus更换android app主题
  6. Android(安卓)闹钟 开发过程记录(二)
  7. 编译源码生成的SDK,创建android project 没有proguard.cfg问题
  8. 导入android源码有错,R.java文件不能自动生成解决方法[转]
  9. android镜像制作方法

随机推荐

  1. 【Android】问题记录2019/3/18:上传APP到G
  2. android studio汉化
  3. Android(安卓)开发之SQLite基础
  4. Android(安卓)Studio将本地计算机文件上
  5. 【Android】Tips for Android(安卓)devel
  6. Android(安卓)快速实现状态栏透明样式
  7. Android(安卓)游戏开发之SurfaceView的简
  8. android 清空之前task中的activity
  9. Android自动生成启动Activity的特定按钮
  10. Android高性能的布局