FileDocCategorySizeDatePackage
AccountManagerService.javaAPI DocAndroid 5.1 API153968Thu Mar 12 22:22:42 GMT 2015com.android.server.accounts

AccountManagerService

public class AccountManagerService extends IAccountManager.Stub implements android.content.pm.RegisteredServicesCacheListener
A system service that provides account, password, and authtoken management for all accounts on the device. Some of these calls are implemented with the help of the corresponding {@link IAccountAuthenticator} services. This service is not accessed by users directly, instead one uses an instance of {@link AccountManager}, which can be accessed as follows: AccountManager accountManager = AccountManager.get(context);
hide

Fields Summary
private static final String
TAG
private static final int
TIMEOUT_DELAY_MS
private static final String
DATABASE_NAME
private static final int
DATABASE_VERSION
private final android.content.Context
mContext
private final android.content.pm.PackageManager
mPackageManager
private android.os.UserManager
mUserManager
private final MessageHandler
mMessageHandler
private static final int
MESSAGE_TIMED_OUT
private static final int
MESSAGE_COPY_SHARED_ACCOUNT
private final IAccountAuthenticatorCache
mAuthenticatorCache
private static final String
TABLE_ACCOUNTS
private static final String
ACCOUNTS_ID
private static final String
ACCOUNTS_NAME
private static final String
ACCOUNTS_TYPE
private static final String
ACCOUNTS_TYPE_COUNT
private static final String
ACCOUNTS_PASSWORD
private static final String
ACCOUNTS_PREVIOUS_NAME
private static final String
TABLE_AUTHTOKENS
private static final String
AUTHTOKENS_ID
private static final String
AUTHTOKENS_ACCOUNTS_ID
private static final String
AUTHTOKENS_TYPE
private static final String
AUTHTOKENS_AUTHTOKEN
private static final String
TABLE_GRANTS
private static final String
GRANTS_ACCOUNTS_ID
private static final String
GRANTS_AUTH_TOKEN_TYPE
private static final String
GRANTS_GRANTEE_UID
private static final String
TABLE_EXTRAS
private static final String
EXTRAS_ID
private static final String
EXTRAS_ACCOUNTS_ID
private static final String
EXTRAS_KEY
private static final String
EXTRAS_VALUE
private static final String
TABLE_META
private static final String
META_KEY
private static final String
META_VALUE
private static final String
TABLE_SHARED_ACCOUNTS
private static final String[]
ACCOUNT_TYPE_COUNT_PROJECTION
private static final android.content.Intent
ACCOUNTS_CHANGED_INTENT
private static final String
COUNT_OF_MATCHING_GRANTS
private static final String
SELECTION_AUTHTOKENS_BY_ACCOUNT
private static final String[]
COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN
private static final String
SELECTION_USERDATA_BY_ACCOUNT
private static final String[]
COLUMNS_EXTRAS_KEY_AND_VALUE
private final LinkedHashMap
mSessions
private final AtomicInteger
mNotificationIds
private final android.util.SparseArray
mUsers
private static AtomicReference
sThis
private static final android.accounts.Account[]
EMPTY_ACCOUNT_ARRAY
Constructors Summary
public AccountManagerService(android.content.Context context)

        this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
    
public AccountManagerService(android.content.Context context, android.content.pm.PackageManager packageManager, IAccountAuthenticatorCache authenticatorCache)

        mContext = context;
        mPackageManager = packageManager;

        mMessageHandler = new MessageHandler(FgThread.get().getLooper());

        mAuthenticatorCache = authenticatorCache;
        mAuthenticatorCache.setListener(this, null /* Handler */);

        sThis.set(this);

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter.addDataScheme("package");
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context1, Intent intent) {
                // Don't delete accounts when updating a authenticator's
                // package.
                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                    purgeOldGrantsAll();
                }
            }
        }, intentFilter);

        IntentFilter userFilter = new IntentFilter();
        userFilter.addAction(Intent.ACTION_USER_REMOVED);
        userFilter.addAction(Intent.ACTION_USER_STARTED);
        mContext.registerReceiverAsUser(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (Intent.ACTION_USER_REMOVED.equals(action)) {
                    onUserRemoved(intent);
                } else if (Intent.ACTION_USER_STARTED.equals(action)) {
                    onUserStarted(intent);
                }
            }
        }, UserHandle.ALL, userFilter, null, null);
    
Methods Summary
public voidaddAccount(android.accounts.IAccountManagerResponse response, java.lang.String accountType, java.lang.String authTokenType, java.lang.String[] requiredFeatures, boolean expectActivityLaunch, android.os.Bundle optionsIn)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "addAccount: accountType " + accountType
                    + ", response " + response
                    + ", authTokenType " + authTokenType
                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
                    + ", expectActivityLaunch " + expectActivityLaunch
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (response == null) throw new IllegalArgumentException("response is null");
        if (accountType == null) throw new IllegalArgumentException("accountType is null");
        checkManageAccountsPermission();

        // Is user disallowed from modifying accounts?
        int userId = Binder.getCallingUserHandle().getIdentifier();
        if (!canUserModifyAccounts(userId)) {
            try {
                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
                        "User is not allowed to add an account!");
            } catch (RemoteException re) {
            }
            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
            return;
        }
        if (!canUserModifyAccountsForType(userId, accountType)) {
            try {
                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
                        "User cannot modify accounts of this type (policy).");
            } catch (RemoteException re) {
            }
            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
                    userId);
            return;
        }

        UserAccounts accounts = getUserAccountsForCaller();
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
        options.putInt(AccountManager.KEY_CALLER_UID, uid);
        options.putInt(AccountManager.KEY_CALLER_PID, pid);

        long identityToken = clearCallingIdentity();
        try {
            new Session(accounts, response, accountType, expectActivityLaunch,
                    true /* stripAuthTokenFromResult */) {
                @Override
                public void run() throws RemoteException {
                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
                            options);
                }

                @Override
                protected String toDebugString(long now) {
                    return super.toDebugString(now) + ", addAccount"
                            + ", accountType " + accountType
                            + ", requiredFeatures "
                            + (requiredFeatures != null
                              ? TextUtils.join(",", requiredFeatures)
                              : null);
                }
            }.bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public voidaddAccountAsUser(android.accounts.IAccountManagerResponse response, java.lang.String accountType, java.lang.String authTokenType, java.lang.String[] requiredFeatures, boolean expectActivityLaunch, android.os.Bundle optionsIn, int userId)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "addAccount: accountType " + accountType
                    + ", response " + response
                    + ", authTokenType " + authTokenType
                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
                    + ", expectActivityLaunch " + expectActivityLaunch
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid()
                    + ", for user id " + userId);
        }
        if (response == null) throw new IllegalArgumentException("response is null");
        if (accountType == null) throw new IllegalArgumentException("accountType is null");
        checkManageAccountsPermission();

        // Only allow the system process to add accounts of other users
        enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
                    + " trying to add account for " + userId);

        // Is user disallowed from modifying accounts?
        if (!canUserModifyAccounts(userId)) {
            try {
                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
                        "User is not allowed to add an account!");
            } catch (RemoteException re) {
            }
            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
            return;
        }
        if (!canUserModifyAccountsForType(userId, accountType)) {
            try {
                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
                        "User cannot modify accounts of this type (policy).");
            } catch (RemoteException re) {
            }
            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
                    userId);
            return;
        }

        UserAccounts accounts = getUserAccounts(userId);
        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
        options.putInt(AccountManager.KEY_CALLER_UID, uid);
        options.putInt(AccountManager.KEY_CALLER_PID, pid);

        long identityToken = clearCallingIdentity();
        try {
            new Session(accounts, response, accountType, expectActivityLaunch,
                    true /* stripAuthTokenFromResult */) {
                @Override
                public void run() throws RemoteException {
                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
                            options);
                }

                @Override
                protected String toDebugString(long now) {
                    return super.toDebugString(now) + ", addAccount"
                            + ", accountType " + accountType
                            + ", requiredFeatures "
                            + (requiredFeatures != null
                              ? TextUtils.join(",", requiredFeatures)
                              : null);
                }
            }.bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public booleanaddAccountExplicitly(android.accounts.Account account, java.lang.String password, android.os.Bundle extras)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "addAccountExplicitly: " + account
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (account == null) throw new IllegalArgumentException("account is null");
        checkAuthenticateAccountsPermission(account);
        /*
         * Child users are not allowed to add accounts. Only the accounts that are
         * shared by the parent profile can be added to child profile.
         *
         * TODO: Only allow accounts that were shared to be added by
         *     a limited user.
         */

        UserAccounts accounts = getUserAccountsForCaller();
        // fails if the account already exists
        long identityToken = clearCallingIdentity();
        try {
            return addAccountInternal(accounts, account, password, extras, false);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private booleanaddAccountInternal(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account, java.lang.String password, android.os.Bundle extras, boolean restricted)

        if (account == null) {
            return false;
        }
        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
            db.beginTransaction();
            try {
                long numMatches = DatabaseUtils.longForQuery(db,
                        "select count(*) from " + TABLE_ACCOUNTS
                                + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
                        new String[]{account.name, account.type});
                if (numMatches > 0) {
                    Log.w(TAG, "insertAccountIntoDatabase: " + account
                            + ", skipping since the account already exists");
                    return false;
                }
                ContentValues values = new ContentValues();
                values.put(ACCOUNTS_NAME, account.name);
                values.put(ACCOUNTS_TYPE, account.type);
                values.put(ACCOUNTS_PASSWORD, password);
                long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
                if (accountId < 0) {
                    Log.w(TAG, "insertAccountIntoDatabase: " + account
                            + ", skipping the DB insert failed");
                    return false;
                }
                if (extras != null) {
                    for (String key : extras.keySet()) {
                        final String value = extras.getString(key);
                        if (insertExtraLocked(db, accountId, key, value) < 0) {
                            Log.w(TAG, "insertAccountIntoDatabase: " + account
                                    + ", skipping since insertExtra failed for key " + key);
                            return false;
                        }
                    }
                }
                db.setTransactionSuccessful();
                insertAccountIntoCacheLocked(accounts, account);
            } finally {
                db.endTransaction();
            }
            sendAccountsChangedBroadcast(accounts.userId);
        }
        if (accounts.userId == UserHandle.USER_OWNER) {
            addAccountToLimitedUsers(account);
        }
        return true;
    
private voidaddAccountToLimitedUsers(android.accounts.Account account)
Adds the account to all limited users as shared accounts. If the user is currently running, then clone the account too.

param
account the account to share with limited users

        List<UserInfo> users = getUserManager().getUsers();
        for (UserInfo user : users) {
            if (user.isRestricted()) {
                addSharedAccountAsUser(account, user.id);
                try {
                    if (ActivityManagerNative.getDefault().isUserRunning(user.id, false)) {
                        mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
                                MESSAGE_COPY_SHARED_ACCOUNT, UserHandle.USER_OWNER, user.id,
                                account));
                    }
                } catch (RemoteException re) {
                    // Shouldn't happen
                }
            }
        }
    
public booleanaddSharedAccountAsUser(android.accounts.Account account, int userId)

        userId = handleIncomingUser(userId);
        SQLiteDatabase db = getUserAccounts(userId).openHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put(ACCOUNTS_NAME, account.name);
        values.put(ACCOUNTS_TYPE, account.type);
        db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
                new String[] {account.name, account.type});
        long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
        if (accountId < 0) {
            Log.w(TAG, "insertAccountIntoDatabase: " + account
                    + ", skipping the DB insert failed");
            return false;
        }
        return true;
    
private booleancanUserModifyAccounts(int userId)

        if (getUserManager().getUserRestrictions(new UserHandle(userId))
                .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
            return false;
        }
        return true;
    
private booleancanUserModifyAccountsForType(int userId, java.lang.String accountType)

        DevicePolicyManager dpm = (DevicePolicyManager) mContext
                .getSystemService(Context.DEVICE_POLICY_SERVICE);
        String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
        if (typesArray == null) {
            return true;
        }
        for (String forbiddenType : typesArray) {
            if (forbiddenType.equals(accountType)) {
                return false;
            }
        }
        return true;
    
protected voidcancelNotification(int id, android.os.UserHandle user)

        long identityToken = clearCallingIdentity();
        try {
            ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
                .cancelAsUser(null, id, user);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private voidcheckAuthenticateAccountsPermission(android.accounts.Account account)

        checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS);
        checkCallingUidAgainstAuthenticator(account);
    
private voidcheckBinderPermission(java.lang.String permissions)
Succeeds if any of the specified permissions are granted.

        final int uid = Binder.getCallingUid();

        for (String perm : permissions) {
            if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
                if (Log.isLoggable(TAG, Log.VERBOSE)) {
                    Log.v(TAG, "  caller uid " + uid + " has " + perm);
                }
                return;
            }
        }

        String msg = "caller uid " + uid + " lacks any of " + TextUtils.join(",", permissions);
        Log.w(TAG, "  " + msg);
        throw new SecurityException(msg);
    
private voidcheckCallingUidAgainstAuthenticator(android.accounts.Account account)

        final int uid = Binder.getCallingUid();
        if (account == null || !hasAuthenticatorUid(account.type, uid)) {
            String msg = "caller uid " + uid + " is different than the authenticator's uid";
            Log.w(TAG, msg);
            throw new SecurityException(msg);
        }
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid");
        }
    
private voidcheckManageAccountsOrUseCredentialsPermissions()

        checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS,
                Manifest.permission.USE_CREDENTIALS);
    
private voidcheckManageAccountsPermission()

        checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS);
    
private voidcheckReadAccountsPermission()

        checkBinderPermission(Manifest.permission.GET_ACCOUNTS);
    
public voidclearPassword(android.accounts.Account account)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "clearPassword: " + account
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (account == null) throw new IllegalArgumentException("account is null");
        checkManageAccountsPermission();
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            setPasswordInternal(accounts, account, null);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private voidcompleteCloningAccount(android.accounts.IAccountManagerResponse response, android.os.Bundle accountCredentials, android.accounts.Account account, com.android.server.accounts.AccountManagerService$UserAccounts targetUser)

        long id = clearCallingIdentity();
        try {
            new Session(targetUser, response, account.type, false,
                    false /* stripAuthTokenFromResult */) {
                @Override
                protected String toDebugString(long now) {
                    return super.toDebugString(now) + ", getAccountCredentialsForClone"
                            + ", " + account.type;
                }

                @Override
                public void run() throws RemoteException {
                    // Confirm that the owner's account still exists before this step.
                    UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER);
                    synchronized (owner.cacheLock) {
                        for (Account acc : getAccounts(UserHandle.USER_OWNER)) {
                            if (acc.equals(account)) {
                                mAuthenticator.addAccountFromCredentials(
                                        this, account, accountCredentials);
                                break;
                            }
                        }
                    }
                }

                @Override
                public void onResult(Bundle result) {
                    // TODO: Anything to do if if succedded?
                    // TODO: If it failed: Show error notification? Should we remove the shadow
                    // account to avoid retries?
                    super.onResult(result);
                }

                @Override
                public void onError(int errorCode, String errorMessage) {
                    super.onError(errorCode,  errorMessage);
                    // TODO: Show error notification to user
                    // TODO: Should we remove the shadow account so that it doesn't keep trying?
                }

            }.bind();
        } finally {
            restoreCallingIdentity(id);
        }
    
public voidconfirmCredentialsAsUser(android.accounts.IAccountManagerResponse response, android.accounts.Account account, android.os.Bundle options, boolean expectActivityLaunch, int userId)

        // Only allow the system process to read accounts of other users
        enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
                    + " trying to confirm account credentials for " + userId);

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "confirmCredentials: " + account
                    + ", response " + response
                    + ", expectActivityLaunch " + expectActivityLaunch
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (response == null) throw new IllegalArgumentException("response is null");
        if (account == null) throw new IllegalArgumentException("account is null");
        checkManageAccountsPermission();
        UserAccounts accounts = getUserAccounts(userId);
        long identityToken = clearCallingIdentity();
        try {
            new Session(accounts, response, account.type, expectActivityLaunch,
                    true /* stripAuthTokenFromResult */) {
                @Override
                public void run() throws RemoteException {
                    mAuthenticator.confirmCredentials(this, account, options);
                }
                @Override
                protected String toDebugString(long now) {
                    return super.toDebugString(now) + ", confirmCredentials"
                            + ", " + account;
                }
            }.bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public voidcopyAccountToUser(android.accounts.IAccountManagerResponse response, android.accounts.Account account, int userFrom, int userTo)

        enforceCrossUserPermission(UserHandle.USER_ALL, "Calling copyAccountToUser requires "
                    + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
        final UserAccounts fromAccounts = getUserAccounts(userFrom);
        final UserAccounts toAccounts = getUserAccounts(userTo);
        if (fromAccounts == null || toAccounts == null) {
            if (response != null) {
                Bundle result = new Bundle();
                result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
                try {
                    response.onResult(result);
                } catch (RemoteException e) {
                    Slog.w(TAG, "Failed to report error back to the client." + e);
                }
            }
            return;
        }

        Slog.d(TAG, "Copying account " + account.name
                + " from user " + userFrom + " to user " + userTo);
        long identityToken = clearCallingIdentity();
        try {
            new Session(fromAccounts, response, account.type, false,
                    false /* stripAuthTokenFromResult */) {
                @Override
                protected String toDebugString(long now) {
                    return super.toDebugString(now) + ", getAccountCredentialsForClone"
                            + ", " + account.type;
                }

                @Override
                public void run() throws RemoteException {
                    mAuthenticator.getAccountCredentialsForCloning(this, account);
                }

                @Override
                public void onResult(Bundle result) {
                    if (result != null
                            && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
                        // Create a Session for the target user and pass in the bundle
                        completeCloningAccount(response, result, account, toAccounts);
                    } else {
                        super.onResult(result);
                    }
                }
            }.bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private voidcreateNoCredentialsPermissionNotification(android.accounts.Account account, android.content.Intent intent, int userId)

        int uid = intent.getIntExtra(
                GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
        String authTokenType = intent.getStringExtra(
                GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
        String authTokenLabel = intent.getStringExtra(
                GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL);

        Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
                0 /* when */);
        final String titleAndSubtitle =
                mContext.getString(R.string.permission_request_notification_with_subtitle,
                account.name);
        final int index = titleAndSubtitle.indexOf('\n");
        String title = titleAndSubtitle;
        String subtitle = "";
        if (index > 0) {
            title = titleAndSubtitle.substring(0, index);
            subtitle = titleAndSubtitle.substring(index + 1);
        }
        UserHandle user = new UserHandle(userId);
        Context contextForUser = getContextForUser(user);
        n.color = contextForUser.getResources().getColor(
                com.android.internal.R.color.system_notification_accent_color);
        n.setLatestEventInfo(contextForUser, title, subtitle,
                PendingIntent.getActivityAsUser(mContext, 0, intent,
                        PendingIntent.FLAG_CANCEL_CURRENT, null, user));
        installNotification(getCredentialPermissionNotificationId(
                account, authTokenType, uid), n, user);
    
private voiddoNotification(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account, java.lang.CharSequence message, android.content.Intent intent, int userId)

        long identityToken = clearCallingIdentity();
        try {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "doNotification: " + message + " intent:" + intent);
            }

            if (intent.getComponent() != null &&
                    GrantCredentialsPermissionActivity.class.getName().equals(
                            intent.getComponent().getClassName())) {
                createNoCredentialsPermissionNotification(account, intent, userId);
            } else {
                final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
                intent.addCategory(String.valueOf(notificationId));
                Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
                        0 /* when */);
                UserHandle user = new UserHandle(userId);
                Context contextForUser = getContextForUser(user);
                final String notificationTitleFormat =
                        contextForUser.getText(R.string.notification_title).toString();
                n.color = contextForUser.getResources().getColor(
                        com.android.internal.R.color.system_notification_accent_color);
                n.setLatestEventInfo(contextForUser,
                        String.format(notificationTitleFormat, account.name),
                        message, PendingIntent.getActivityAsUser(
                        mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
                        null, user));
                installNotification(notificationId, n, user);
            }
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
protected voiddump(java.io.FileDescriptor fd, java.io.PrintWriter fout, java.lang.String[] args)

        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                != PackageManager.PERMISSION_GRANTED) {
            fout.println("Permission Denial: can't dump AccountsManager from from pid="
                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
                    + " without permission " + android.Manifest.permission.DUMP);
            return;
        }
        final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
        final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, "  ");

        final List<UserInfo> users = getUserManager().getUsers();
        for (UserInfo user : users) {
            ipw.println("User " + user + ":");
            ipw.increaseIndent();
            dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
            ipw.println();
            ipw.decreaseIndent();
        }
    
private voiddumpUser(com.android.server.accounts.AccountManagerService$UserAccounts userAccounts, java.io.FileDescriptor fd, java.io.PrintWriter fout, java.lang.String[] args, boolean isCheckinRequest)

        synchronized (userAccounts.cacheLock) {
            final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();

            if (isCheckinRequest) {
                // This is a checkin request. *Only* upload the account types and the count of each.
                Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
                        null, null, ACCOUNTS_TYPE, null, null);
                try {
                    while (cursor.moveToNext()) {
                        // print type,count
                        fout.println(cursor.getString(0) + "," + cursor.getString(1));
                    }
                } finally {
                    if (cursor != null) {
                        cursor.close();
                    }
                }
            } else {
                Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
                        Process.myUid(), null);
                fout.println("Accounts: " + accounts.length);
                for (Account account : accounts) {
                    fout.println("  " + account);
                }

                fout.println();
                synchronized (mSessions) {
                    final long now = SystemClock.elapsedRealtime();
                    fout.println("Active Sessions: " + mSessions.size());
                    for (Session session : mSessions.values()) {
                        fout.println("  " + session.toDebugString(now));
                    }
                }

                fout.println();
                mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
            }
        }
    
public voideditProperties(android.accounts.IAccountManagerResponse response, java.lang.String accountType, boolean expectActivityLaunch)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "editProperties: accountType " + accountType
                    + ", response " + response
                    + ", expectActivityLaunch " + expectActivityLaunch
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (response == null) throw new IllegalArgumentException("response is null");
        if (accountType == null) throw new IllegalArgumentException("accountType is null");
        checkManageAccountsPermission();
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            new Session(accounts, response, accountType, expectActivityLaunch,
                    true /* stripAuthTokenFromResult */) {
                @Override
                public void run() throws RemoteException {
                    mAuthenticator.editProperties(this, mAccountType);
                }
                @Override
                protected String toDebugString(long now) {
                    return super.toDebugString(now) + ", editProperties"
                            + ", accountType " + accountType;
                }
            }.bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private voidenforceCrossUserPermission(int userId, java.lang.String errorMessage)

        if (userId != UserHandle.getCallingUserId()
                && Binder.getCallingUid() != Process.myUid()
                && mContext.checkCallingOrSelfPermission(
                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
                    != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException(errorMessage);
        }
    
private android.accounts.Account[]filterSharedAccounts(com.android.server.accounts.AccountManagerService$UserAccounts userAccounts, android.accounts.Account[] unfiltered, int callingUid, java.lang.String callingPackage)

        if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
                || callingUid == Process.myUid()) {
            return unfiltered;
        }
        UserInfo user = mUserManager.getUserInfo(userAccounts.userId);
        if (user != null && user.isRestricted()) {
            String[] packages = mPackageManager.getPackagesForUid(callingUid);
            // If any of the packages is a white listed package, return the full set,
            // otherwise return non-shared accounts only.
            // This might be a temporary way to specify a whitelist
            String whiteList = mContext.getResources().getString(
                    com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
            for (String packageName : packages) {
                if (whiteList.contains(";" + packageName + ";")) {
                    return unfiltered;
                }
            }
            ArrayList<Account> allowed = new ArrayList<Account>();
            Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
            if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
            String requiredAccountType = "";
            try {
                // If there's an explicit callingPackage specified, check if that package
                // opted in to see restricted accounts.
                if (callingPackage != null) {
                    PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
                    if (pi != null && pi.restrictedAccountType != null) {
                        requiredAccountType = pi.restrictedAccountType;
                    }
                } else {
                    // Otherwise check if the callingUid has a package that has opted in
                    for (String packageName : packages) {
                        PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
                        if (pi != null && pi.restrictedAccountType != null) {
                            requiredAccountType = pi.restrictedAccountType;
                            break;
                        }
                    }
                }
            } catch (NameNotFoundException nnfe) {
            }
            for (Account account : unfiltered) {
                if (account.type.equals(requiredAccountType)) {
                    allowed.add(account);
                } else {
                    boolean found = false;
                    for (Account shared : sharedAccounts) {
                        if (shared.equals(account)) {
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        allowed.add(account);
                    }
                }
            }
            Account[] filtered = new Account[allowed.size()];
            allowed.toArray(filtered);
            return filtered;
        } else {
            return unfiltered;
        }
    
private longgetAccountIdLocked(android.database.sqlite.SQLiteDatabase db, android.accounts.Account account)

        Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
                "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
        try {
            if (cursor.moveToNext()) {
                return cursor.getLong(0);
            }
            return -1;
        } finally {
            cursor.close();
        }
    
public android.accounts.Account[]getAccounts(int userId)
Returns the accounts for a specific user

hide

        checkReadAccountsPermission();
        UserAccounts accounts = getUserAccounts(userId);
        int callingUid = Binder.getCallingUid();
        long identityToken = clearCallingIdentity();
        try {
            synchronized (accounts.cacheLock) {
                return getAccountsFromCacheLocked(accounts, null, callingUid, null);
            }
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private android.accounts.AccountAndUser[]getAccounts(int[] userIds)

        final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
        for (int userId : userIds) {
            UserAccounts userAccounts = getUserAccounts(userId);
            if (userAccounts == null) continue;
            synchronized (userAccounts.cacheLock) {
                Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
                        Binder.getCallingUid(), null);
                for (int a = 0; a < accounts.length; a++) {
                    runningAccounts.add(new AccountAndUser(accounts[a], userId));
                }
            }
        }

        AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
        return runningAccounts.toArray(accountsArray);
    
public android.accounts.Account[]getAccounts(java.lang.String type)

        return getAccountsAsUser(type, UserHandle.getCallingUserId());
    
public android.accounts.Account[]getAccountsAsUser(java.lang.String type, int userId)

        return getAccountsAsUser(type, userId, null, -1);
    
private android.accounts.Account[]getAccountsAsUser(java.lang.String type, int userId, java.lang.String callingPackage, int packageUid)

        int callingUid = Binder.getCallingUid();
        // Only allow the system process to read accounts of other users
        if (userId != UserHandle.getCallingUserId()
                && callingUid != Process.myUid()
                && mContext.checkCallingOrSelfPermission(
                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
                    != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("User " + UserHandle.getCallingUserId()
                    + " trying to get account for " + userId);
        }

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "getAccounts: accountType " + type
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        // If the original calling app was using the framework account chooser activity, we'll
        // be passed in the original caller's uid here, which is what should be used for filtering.
        if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
            callingUid = packageUid;
        }
        checkReadAccountsPermission();
        UserAccounts accounts = getUserAccounts(userId);
        long identityToken = clearCallingIdentity();
        try {
            synchronized (accounts.cacheLock) {
                return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage);
            }
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public voidgetAccountsByFeatures(android.accounts.IAccountManagerResponse response, java.lang.String type, java.lang.String[] features)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "getAccounts: accountType " + type
                    + ", response " + response
                    + ", features " + stringArrayToString(features)
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (response == null) throw new IllegalArgumentException("response is null");
        if (type == null) throw new IllegalArgumentException("accountType is null");
        checkReadAccountsPermission();
        UserAccounts userAccounts = getUserAccountsForCaller();
        int callingUid = Binder.getCallingUid();
        long identityToken = clearCallingIdentity();
        try {
            if (features == null || features.length == 0) {
                Account[] accounts;
                synchronized (userAccounts.cacheLock) {
                    accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
                }
                Bundle result = new Bundle();
                result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
                onResult(response, result);
                return;
            }
            new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features,
                    callingUid).bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public android.accounts.Account[]getAccountsByTypeForPackage(java.lang.String type, java.lang.String packageName)

        checkBinderPermission(android.Manifest.permission.INTERACT_ACROSS_USERS);
        int packageUid = -1;
        try {
            packageUid = AppGlobals.getPackageManager().getPackageUid(
                    packageName, UserHandle.getCallingUserId());
        } catch (RemoteException re) {
            Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
            return new Account[0];
        }
        return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName, packageUid);
    
public android.accounts.Account[]getAccountsForPackage(java.lang.String packageName, int uid)

        int callingUid = Binder.getCallingUid();
        if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
            throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
                    + callingUid + " with uid=" + uid);
        }
        return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid);
    
protected android.accounts.Account[]getAccountsFromCacheLocked(com.android.server.accounts.AccountManagerService$UserAccounts userAccounts, java.lang.String accountType, int callingUid, java.lang.String callingPackage)

        if (accountType != null) {
            final Account[] accounts = userAccounts.accountCache.get(accountType);
            if (accounts == null) {
                return EMPTY_ACCOUNT_ARRAY;
            } else {
                return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
                        callingUid, callingPackage);
            }
        } else {
            int totalLength = 0;
            for (Account[] accounts : userAccounts.accountCache.values()) {
                totalLength += accounts.length;
            }
            if (totalLength == 0) {
                return EMPTY_ACCOUNT_ARRAY;
            }
            Account[] accounts = new Account[totalLength];
            totalLength = 0;
            for (Account[] accountsOfType : userAccounts.accountCache.values()) {
                System.arraycopy(accountsOfType, 0, accounts, totalLength,
                        accountsOfType.length);
                totalLength += accountsOfType.length;
            }
            return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
        }
    
public android.accounts.AccountAndUser[]getAllAccounts()
{@hide}

        final List<UserInfo> users = getUserManager().getUsers();
        final int[] userIds = new int[users.size()];
        for (int i = 0; i < userIds.length; i++) {
            userIds[i] = users.get(i).id;
        }
        return getAccounts(userIds);
    
public voidgetAuthToken(android.accounts.IAccountManagerResponse response, android.accounts.Account account, java.lang.String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch, android.os.Bundle loginOptionsIn)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "getAuthToken: " + account
                    + ", response " + response
                    + ", authTokenType " + authTokenType
                    + ", notifyOnAuthFailure " + notifyOnAuthFailure
                    + ", expectActivityLaunch " + expectActivityLaunch
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (response == null) throw new IllegalArgumentException("response is null");
        try {
            if (account == null) {
                Slog.w(TAG, "getAuthToken called with null account");
                response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
                return;
            }
            if (authTokenType == null) {
                Slog.w(TAG, "getAuthToken called with null authTokenType");
                response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
                return;
            }
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed to report error back to the client." + e);
            return;
        }

        checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
        final UserAccounts accounts = getUserAccountsForCaller();
        final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
        authenticatorInfo = mAuthenticatorCache.getServiceInfo(
                AuthenticatorDescription.newKey(account.type), accounts.userId);
        final boolean customTokens =
            authenticatorInfo != null && authenticatorInfo.type.customTokens;

        // skip the check if customTokens
        final int callerUid = Binder.getCallingUid();
        final boolean permissionGranted = customTokens ||
            permissionIsGranted(account, authTokenType, callerUid);

        final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() :
            loginOptionsIn;
        // let authenticator know the identity of the caller
        loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
        loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
        if (notifyOnAuthFailure) {
            loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
        }

        long identityToken = clearCallingIdentity();
        try {
            // if the caller has permission, do the peek. otherwise go the more expensive
            // route of starting a Session
            if (!customTokens && permissionGranted) {
                String authToken = readAuthTokenInternal(accounts, account, authTokenType);
                if (authToken != null) {
                    Bundle result = new Bundle();
                    result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
                    result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
                    result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
                    onResult(response, result);
                    return;
                }
            }

            new Session(accounts, response, account.type, expectActivityLaunch,
                    false /* stripAuthTokenFromResult */) {
                @Override
                protected String toDebugString(long now) {
                    if (loginOptions != null) loginOptions.keySet();
                    return super.toDebugString(now) + ", getAuthToken"
                            + ", " + account
                            + ", authTokenType " + authTokenType
                            + ", loginOptions " + loginOptions
                            + ", notifyOnAuthFailure " + notifyOnAuthFailure;
                }

                @Override
                public void run() throws RemoteException {
                    // If the caller doesn't have permission then create and return the
                    // "grant permission" intent instead of the "getAuthToken" intent.
                    if (!permissionGranted) {
                        mAuthenticator.getAuthTokenLabel(this, authTokenType);
                    } else {
                        mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
                    }
                }

                @Override
                public void onResult(Bundle result) {
                    if (result != null) {
                        if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
                            Intent intent = newGrantCredentialsPermissionIntent(account, callerUid,
                                    new AccountAuthenticatorResponse(this),
                                    authTokenType,
                                    result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL));
                            Bundle bundle = new Bundle();
                            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
                            onResult(bundle);
                            return;
                        }
                        String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
                        if (authToken != null) {
                            String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
                            String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
                            if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
                                onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
                                        "the type and name should not be empty");
                                return;
                            }
                            if (!customTokens) {
                                saveAuthTokenToDatabase(mAccounts, new Account(name, type),
                                        authTokenType, authToken);
                            }
                        }

                        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
                        if (intent != null && notifyOnAuthFailure && !customTokens) {
                            doNotification(mAccounts,
                                    account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
                                    intent, accounts.userId);
                        }
                    }
                    super.onResult(result);
                }
            }.bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public voidgetAuthTokenLabel(android.accounts.IAccountManagerResponse response, java.lang.String accountType, java.lang.String authTokenType)

        if (accountType == null) throw new IllegalArgumentException("accountType is null");
        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");

        final int callingUid = getCallingUid();
        clearCallingIdentity();
        if (callingUid != Process.SYSTEM_UID) {
            throw new SecurityException("can only call from system");
        }
        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
        long identityToken = clearCallingIdentity();
        try {
            new Session(accounts, response, accountType, false,
                    false /* stripAuthTokenFromResult */) {
                @Override
                protected String toDebugString(long now) {
                    return super.toDebugString(now) + ", getAuthTokenLabel"
                            + ", " + accountType
                            + ", authTokenType " + authTokenType;
                }

                @Override
                public void run() throws RemoteException {
                    mAuthenticator.getAuthTokenLabel(this, authTokenType);
                }

                @Override
                public void onResult(Bundle result) {
                    if (result != null) {
                        String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
                        Bundle bundle = new Bundle();
                        bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
                        super.onResult(bundle);
                        return;
                    } else {
                        super.onResult(result);
                    }
                }
            }.bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public android.accounts.AuthenticatorDescription[]getAuthenticatorTypes(int userId)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "getAuthenticatorTypes: "
                    + "for user id " + userId
                    + "caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        // Only allow the system process to read accounts of other users
        enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
                + " trying get authenticator types for " + userId);
        final long identityToken = clearCallingIdentity();
        try {
            Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
                    authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
            AuthenticatorDescription[] types =
                    new AuthenticatorDescription[authenticatorCollection.size()];
            int i = 0;
            for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
                    : authenticatorCollection) {
                types[i] = authenticator.type;
                i++;
            }
            return types;
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private android.content.ContextgetContextForUser(android.os.UserHandle user)

        try {
            return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
        } catch (NameNotFoundException e) {
            // Default to mContext, not finding the package system is running as is unlikely.
            return mContext;
        }
    
private java.lang.IntegergetCredentialPermissionNotificationId(android.accounts.Account account, java.lang.String authTokenType, int uid)

        Integer id;
        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
        synchronized (accounts.credentialsPermissionNotificationIds) {
            final Pair<Pair<Account, String>, Integer> key =
                    new Pair<Pair<Account, String>, Integer>(
                            new Pair<Account, String>(account, authTokenType), uid);
            id = accounts.credentialsPermissionNotificationIds.get(key);
            if (id == null) {
                id = mNotificationIds.incrementAndGet();
                accounts.credentialsPermissionNotificationIds.put(key, id);
            }
        }
        return id;
    
private static java.lang.StringgetDatabaseName(int userId)

        File systemDir = Environment.getSystemSecureDirectory();
        File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
        if (userId == 0) {
            // Migrate old file, if it exists, to the new location.
            // Make sure the new file doesn't already exist. A dummy file could have been
            // accidentally created in the old location, causing the new one to become corrupted
            // as well.
            File oldFile = new File(systemDir, DATABASE_NAME);
            if (oldFile.exists() && !databaseFile.exists()) {
                // Check for use directory; create if it doesn't exist, else renameTo will fail
                File userDir = Environment.getUserSystemDirectory(userId);
                if (!userDir.exists()) {
                    if (!userDir.mkdirs()) {
                        throw new IllegalStateException("User dir cannot be created: " + userDir);
                    }
                }
                if (!oldFile.renameTo(databaseFile)) {
                    throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
                }
            }
        }
        return databaseFile.getPath();
    
private longgetExtrasIdLocked(android.database.sqlite.SQLiteDatabase db, long accountId, java.lang.String key)

        Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
                EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
                new String[]{key}, null, null, null);
        try {
            if (cursor.moveToNext()) {
                return cursor.getLong(0);
            }
            return -1;
        } finally {
            cursor.close();
        }
    
public java.lang.StringgetPassword(android.accounts.Account account)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "getPassword: " + account
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (account == null) throw new IllegalArgumentException("account is null");
        checkAuthenticateAccountsPermission(account);

        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            return readPasswordInternal(accounts, account);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public java.lang.StringgetPreviousName(android.accounts.Account account)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "getPreviousName: " + account
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (account == null) throw new IllegalArgumentException("account is null");
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            return readPreviousNameInternal(accounts, account);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public android.accounts.AccountAndUser[]getRunningAccounts()
Returns accounts for all running users.

hide

        final int[] runningUserIds;
        try {
            runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
        } catch (RemoteException e) {
            // Running in system_server; should never happen
            throw new RuntimeException(e);
        }
        return getAccounts(runningUserIds);
    
public android.accounts.Account[]getSharedAccountsAsUser(int userId)

        userId = handleIncomingUser(userId);
        UserAccounts accounts = getUserAccounts(userId);
        ArrayList<Account> accountList = new ArrayList<Account>();
        Cursor cursor = null;
        try {
            cursor = accounts.openHelper.getReadableDatabase()
                    .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
                    null, null, null, null, null);
            if (cursor != null && cursor.moveToFirst()) {
                int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
                int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
                do {
                    accountList.add(new Account(cursor.getString(nameIndex),
                            cursor.getString(typeIndex)));
                } while (cursor.moveToNext());
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        Account[] accountArray = new Account[accountList.size()];
        accountList.toArray(accountArray);
        return accountArray;
    
private java.lang.IntegergetSigninRequiredNotificationId(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account)

        Integer id;
        synchronized (accounts.signinRequiredNotificationIds) {
            id = accounts.signinRequiredNotificationIds.get(account);
            if (id == null) {
                id = mNotificationIds.incrementAndGet();
                accounts.signinRequiredNotificationIds.put(account, id);
            }
        }
        return id;
    
public static com.android.server.accounts.AccountManagerServicegetSingleton()
This should only be called by system code. One should only call this after the service has started.

return
a reference to the AccountManagerService instance
hide


     
        ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
        ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    
        return sThis.get();
    
protected com.android.server.accounts.AccountManagerService$UserAccountsgetUserAccounts(int userId)

        synchronized (mUsers) {
            UserAccounts accounts = mUsers.get(userId);
            if (accounts == null) {
                accounts = initUserLocked(userId);
                mUsers.append(userId, accounts);
            }
            return accounts;
        }
    
private com.android.server.accounts.AccountManagerService$UserAccountsgetUserAccountsForCaller()

        return getUserAccounts(UserHandle.getCallingUserId());
    
public java.lang.StringgetUserData(android.accounts.Account account, java.lang.String key)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "getUserData: " + account
                    + ", key " + key
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (account == null) throw new IllegalArgumentException("account is null");
        if (key == null) throw new IllegalArgumentException("key is null");
        checkAuthenticateAccountsPermission(account);
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            return readUserDataInternal(accounts, account, key);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private android.os.UserManagergetUserManager()

        if (mUserManager == null) {
            mUserManager = UserManager.get(mContext);
        }
        return mUserManager;
    
private voidgrantAppPermission(android.accounts.Account account, java.lang.String authTokenType, int uid)
Allow callers with the given uid permission to get credentials for account/authTokenType.

Although this is public it can only be accessed via the AccountManagerService object which is in the system. This means we don't need to protect it with permissions.

hide

        if (account == null || authTokenType == null) {
            Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
            return;
        }
        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
            db.beginTransaction();
            try {
                long accountId = getAccountIdLocked(db, account);
                if (accountId >= 0) {
                    ContentValues values = new ContentValues();
                    values.put(GRANTS_ACCOUNTS_ID, accountId);
                    values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
                    values.put(GRANTS_GRANTEE_UID, uid);
                    db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
                    db.setTransactionSuccessful();
                }
            } finally {
                db.endTransaction();
            }
            cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
                    new UserHandle(accounts.userId));
        }
    
private inthandleIncomingUser(int userId)

        try {
            return ActivityManagerNative.getDefault().handleIncomingUser(
                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
        } catch (RemoteException re) {
            // Shouldn't happen, local.
        }
        return userId;
    
private booleanhasAuthenticatorUid(java.lang.String accountType, int callingUid)

        final int callingUserId = UserHandle.getUserId(callingUid);
        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
                mAuthenticatorCache.getAllServices(callingUserId)) {
            if (serviceInfo.type.type.equals(accountType)) {
                return (serviceInfo.uid == callingUid) ||
                        (mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
                                == PackageManager.SIGNATURE_MATCH);
            }
        }
        return false;
    
private booleanhasExplicitlyGrantedPermission(android.accounts.Account account, java.lang.String authTokenType, int callerUid)

        if (callerUid == Process.SYSTEM_UID) {
            return true;
        }
        UserAccounts accounts = getUserAccountsForCaller();
        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
            String[] args = { String.valueOf(callerUid), authTokenType,
                    account.name, account.type};
            final boolean permissionGranted =
                    DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
            if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
                // TODO: Skip this check when running automated tests. Replace this
                // with a more general solution.
                Log.d(TAG, "no credentials permission for usage of " + account + ", "
                        + authTokenType + " by uid " + callerUid
                        + " but ignoring since device is in test harness.");
                return true;
            }
            return permissionGranted;
        }
    
public voidhasFeatures(android.accounts.IAccountManagerResponse response, android.accounts.Account account, java.lang.String[] features)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "hasFeatures: " + account
                    + ", response " + response
                    + ", features " + stringArrayToString(features)
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (response == null) throw new IllegalArgumentException("response is null");
        if (account == null) throw new IllegalArgumentException("account is null");
        if (features == null) throw new IllegalArgumentException("features is null");
        checkReadAccountsPermission();
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            new TestFeaturesSession(accounts, response, account, features).bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private com.android.server.accounts.AccountManagerService$UserAccountsinitUserLocked(int userId)

        UserAccounts accounts = mUsers.get(userId);
        if (accounts == null) {
            accounts = new UserAccounts(mContext, userId);
            mUsers.append(userId, accounts);
            purgeOldGrants(accounts);
            validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
        }
        return accounts;
    
private voidinsertAccountIntoCacheLocked(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account)
This assumes that the caller has already checked that the account is not already present.

        Account[] accountsForType = accounts.accountCache.get(account.type);
        int oldLength = (accountsForType != null) ? accountsForType.length : 0;
        Account[] newAccountsForType = new Account[oldLength + 1];
        if (accountsForType != null) {
            System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
        }
        newAccountsForType[oldLength] = account;
        accounts.accountCache.put(account.type, newAccountsForType);
    
private longinsertExtraLocked(android.database.sqlite.SQLiteDatabase db, long accountId, java.lang.String key, java.lang.String value)

        ContentValues values = new ContentValues();
        values.put(EXTRAS_KEY, key);
        values.put(EXTRAS_ACCOUNTS_ID, accountId);
        values.put(EXTRAS_VALUE, value);
        return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
    
protected voidinstallNotification(int notificationId, android.app.Notification n, android.os.UserHandle user)

        ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
                .notifyAsUser(null, notificationId, n, user);
    
public voidinvalidateAuthToken(java.lang.String accountType, java.lang.String authToken)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "invalidateAuthToken: accountType " + accountType
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (accountType == null) throw new IllegalArgumentException("accountType is null");
        if (authToken == null) throw new IllegalArgumentException("authToken is null");
        checkManageAccountsOrUseCredentialsPermissions();
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            synchronized (accounts.cacheLock) {
                final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
                db.beginTransaction();
                try {
                    invalidateAuthTokenLocked(accounts, db, accountType, authToken);
                    db.setTransactionSuccessful();
                } finally {
                    db.endTransaction();
                }
            }
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private voidinvalidateAuthTokenLocked(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.database.sqlite.SQLiteDatabase db, java.lang.String accountType, java.lang.String authToken)

        if (authToken == null || accountType == null) {
            return;
        }
        Cursor cursor = db.rawQuery(
                "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
                        + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
                        + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
                        + " FROM " + TABLE_ACCOUNTS
                        + " JOIN " + TABLE_AUTHTOKENS
                        + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
                        + " = " + AUTHTOKENS_ACCOUNTS_ID
                        + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
                        + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
                new String[]{authToken, accountType});
        try {
            while (cursor.moveToNext()) {
                long authTokenId = cursor.getLong(0);
                String accountName = cursor.getString(1);
                String authTokenType = cursor.getString(2);
                db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
                writeAuthTokenIntoCacheLocked(accounts, db, new Account(accountName, accountType),
                        authTokenType, null);
            }
        } finally {
            cursor.close();
        }
    
private booleanisPrivileged(int callingUid)

        final int callingUserId = UserHandle.getUserId(callingUid);

        final PackageManager userPackageManager;
        try {
            userPackageManager = mContext.createPackageContextAsUser(
                    "android", 0, new UserHandle(callingUserId)).getPackageManager();
        } catch (NameNotFoundException e) {
            return false;
        }

        String[] packages = userPackageManager.getPackagesForUid(callingUid);
        for (String name : packages) {
            try {
                PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
                if (packageInfo != null
                        && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {
                    return true;
                }
            } catch (PackageManager.NameNotFoundException e) {
                return false;
            }
        }
        return false;
    
private android.content.IntentnewGrantCredentialsPermissionIntent(android.accounts.Account account, int uid, android.accounts.AccountAuthenticatorResponse response, java.lang.String authTokenType, java.lang.String authTokenLabel)


        Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
        // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
        // Since it was set in Eclair+ we can't change it without breaking apps using
        // the intent from a non-Activity context.
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addCategory(
                String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));

        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);

        return intent;
    
public android.os.IBinderonBind(android.content.Intent intent)

        return asBinder();
    
private voidonResult(android.accounts.IAccountManagerResponse response, android.os.Bundle result)

        if (result == null) {
            Log.e(TAG, "the result is unexpectedly null", new Exception());
        }
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
                    + response);
        }
        try {
            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);
            }
        }
    
public voidonServiceChanged(android.accounts.AuthenticatorDescription desc, int userId, boolean removed)

        validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
    
public booleanonTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)

        try {
            return super.onTransact(code, data, reply, flags);
        } catch (RuntimeException e) {
            // The account manager only throws security exceptions, so let's
            // log all others.
            if (!(e instanceof SecurityException)) {
                Slog.wtf(TAG, "Account Manager Crash", e);
            }
            throw e;
        }
    
private voidonUserRemoved(android.content.Intent intent)

        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
        if (userId < 1) return;

        UserAccounts accounts;
        synchronized (mUsers) {
            accounts = mUsers.get(userId);
            mUsers.remove(userId);
        }
        if (accounts == null) {
            File dbFile = new File(getDatabaseName(userId));
            dbFile.delete();
            return;
        }

        synchronized (accounts.cacheLock) {
            accounts.openHelper.close();
            File dbFile = new File(getDatabaseName(userId));
            dbFile.delete();
        }
    
private voidonUserStarted(android.content.Intent intent)

        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
        if (userId < 1) return;

        // Check if there's a shared account that needs to be created as an account
        Account[] sharedAccounts = getSharedAccountsAsUser(userId);
        if (sharedAccounts == null || sharedAccounts.length == 0) return;
        Account[] accounts = getAccountsAsUser(null, userId);
        for (Account sa : sharedAccounts) {
            if (ArrayUtils.contains(accounts, sa)) continue;
            // Account doesn't exist. Copy it now.
            copyAccountToUser(null /*no response*/, sa, UserHandle.USER_OWNER, userId);
        }
    
public java.lang.StringpeekAuthToken(android.accounts.Account account, java.lang.String authTokenType)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "peekAuthToken: " + account
                    + ", authTokenType " + authTokenType
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (account == null) throw new IllegalArgumentException("account is null");
        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
        checkAuthenticateAccountsPermission(account);
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            return readAuthTokenInternal(accounts, account, authTokenType);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private booleanpermissionIsGranted(android.accounts.Account account, java.lang.String authTokenType, int callerUid)

        final boolean isPrivileged = isPrivileged(callerUid);
        final boolean fromAuthenticator = account != null
                && hasAuthenticatorUid(account.type, callerUid);
        final boolean hasExplicitGrants = account != null
                && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
                    + callerUid + ", " + account
                    + ": is authenticator? " + fromAuthenticator
                    + ", has explicit permission? " + hasExplicitGrants);
        }
        return fromAuthenticator || hasExplicitGrants || isPrivileged;
    
private voidpurgeOldGrants(com.android.server.accounts.AccountManagerService$UserAccounts accounts)

        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
            final Cursor cursor = db.query(TABLE_GRANTS,
                    new String[]{GRANTS_GRANTEE_UID},
                    null, null, GRANTS_GRANTEE_UID, null, null);
            try {
                while (cursor.moveToNext()) {
                    final int uid = cursor.getInt(0);
                    final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
                    if (packageExists) {
                        continue;
                    }
                    Log.d(TAG, "deleting grants for UID " + uid
                            + " because its package is no longer installed");
                    db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
                            new String[]{Integer.toString(uid)});
                }
            } finally {
                cursor.close();
            }
        }
    
private voidpurgeOldGrantsAll()

        synchronized (mUsers) {
            for (int i = 0; i < mUsers.size(); i++) {
                purgeOldGrants(mUsers.valueAt(i));
            }
        }
    
protected java.lang.StringreadAuthTokenInternal(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account, java.lang.String authTokenType)

        synchronized (accounts.cacheLock) {
            HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
            if (authTokensForAccount == null) {
                // need to populate the cache for this account
                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
                authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
                accounts.authTokenCache.put(account, authTokensForAccount);
            }
            return authTokensForAccount.get(authTokenType);
        }
    
protected java.util.HashMapreadAuthTokensForAccountFromDatabaseLocked(android.database.sqlite.SQLiteDatabase db, android.accounts.Account account)

        HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
        Cursor cursor = db.query(TABLE_AUTHTOKENS,
                COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
                SELECTION_AUTHTOKENS_BY_ACCOUNT,
                new String[]{account.name, account.type},
                null, null, null);
        try {
            while (cursor.moveToNext()) {
                final String type = cursor.getString(0);
                final String authToken = cursor.getString(1);
                authTokensForAccount.put(type, authToken);
            }
        } finally {
            cursor.close();
        }
        return authTokensForAccount;
    
private java.lang.StringreadPasswordInternal(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account)

        if (account == null) {
            return null;
        }

        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
            Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
                    new String[]{account.name, account.type}, null, null, null);
            try {
                if (cursor.moveToNext()) {
                    return cursor.getString(0);
                }
                return null;
            } finally {
                cursor.close();
            }
        }
    
private java.lang.StringreadPreviousNameInternal(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account)

        if  (account == null) {
            return null;
        }
        synchronized (accounts.cacheLock) {
            AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
            if (previousNameRef == null) {
                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
                Cursor cursor = db.query(
                        TABLE_ACCOUNTS,
                        new String[]{ ACCOUNTS_PREVIOUS_NAME },
                        ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
                        new String[] { account.name, account.type },
                        null,
                        null,
                        null);
                try {
                    if (cursor.moveToNext()) {
                        String previousName = cursor.getString(0);
                        previousNameRef = new AtomicReference<String>(previousName);
                        accounts.previousNameCache.put(account, previousNameRef);
                        return previousName;
                    } else {
                        return null;
                    }
                } finally {
                    cursor.close();
                }
            } else {
                return previousNameRef.get();
            }
        }
    
protected java.util.HashMapreadUserDataForAccountFromDatabaseLocked(android.database.sqlite.SQLiteDatabase db, android.accounts.Account account)

        HashMap<String, String> userDataForAccount = new HashMap<String, String>();
        Cursor cursor = db.query(TABLE_EXTRAS,
                COLUMNS_EXTRAS_KEY_AND_VALUE,
                SELECTION_USERDATA_BY_ACCOUNT,
                new String[]{account.name, account.type},
                null, null, null);
        try {
            while (cursor.moveToNext()) {
                final String tmpkey = cursor.getString(0);
                final String value = cursor.getString(1);
                userDataForAccount.put(tmpkey, value);
            }
        } finally {
            cursor.close();
        }
        return userDataForAccount;
    
protected java.lang.StringreadUserDataInternal(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account, java.lang.String key)

        synchronized (accounts.cacheLock) {
            HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
            if (userDataForAccount == null) {
                // need to populate the cache for this account
                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
                userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
                accounts.userDataCache.put(account, userDataForAccount);
            }
            return userDataForAccount.get(key);
        }
    
public voidremoveAccount(android.accounts.IAccountManagerResponse response, android.accounts.Account account, boolean expectActivityLaunch)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "removeAccount: " + account
                    + ", response " + response
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (response == null) throw new IllegalArgumentException("response is null");
        if (account == null) throw new IllegalArgumentException("account is null");
        checkManageAccountsPermission();
        UserHandle user = Binder.getCallingUserHandle();
        UserAccounts accounts = getUserAccountsForCaller();
        int userId = Binder.getCallingUserHandle().getIdentifier();
        if (!canUserModifyAccounts(userId)) {
            try {
                // TODO: This should be ERROR_CODE_USER_RESTRICTED instead. See http://b/16322768
                response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
                        "User cannot modify accounts");
            } catch (RemoteException re) {
            }
            return;
        }
        if (!canUserModifyAccountsForType(userId, account.type)) {
            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();

        cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
        synchronized (accounts.credentialsPermissionNotificationIds) {
            for (Pair<Pair<Account, String>, Integer> pair:
                accounts.credentialsPermissionNotificationIds.keySet()) {
                if (account.equals(pair.first.first)) {
                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
                    cancelNotification(id, user);
                }
            }
        }

        try {
            new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public voidremoveAccountAsUser(android.accounts.IAccountManagerResponse response, android.accounts.Account account, boolean expectActivityLaunch, int userId)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "removeAccount: " + account
                    + ", response " + response
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid()
                    + ", for user id " + userId);
        }
        if (response == null) throw new IllegalArgumentException("response is null");
        if (account == null) throw new IllegalArgumentException("account is null");

        // Only allow the system process to modify accounts of other users
        enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
                    + " trying to remove account for " + userId);
        checkManageAccountsPermission();

        UserAccounts accounts = getUserAccounts(userId);
        if (!canUserModifyAccounts(userId)) {
            try {
                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
                        "User cannot modify accounts");
            } catch (RemoteException re) {
            }
            return;
        }
        if (!canUserModifyAccountsForType(userId, account.type)) {
            try {
                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
                        "User cannot modify accounts of this type (policy).");
            } catch (RemoteException re) {
            }
            return;
        }

        UserHandle user = new UserHandle(userId);
        long identityToken = clearCallingIdentity();

        cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
        synchronized(accounts.credentialsPermissionNotificationIds) {
            for (Pair<Pair<Account, String>, Integer> pair:
                accounts.credentialsPermissionNotificationIds.keySet()) {
                if (account.equals(pair.first.first)) {
                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
                    cancelNotification(id, user);
                }
            }
        }

        try {
            new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public booleanremoveAccountExplicitly(android.accounts.Account account)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "removeAccountExplicitly: " + account
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (account == null) throw new IllegalArgumentException("account is null");
        checkAuthenticateAccountsPermission(account);

        UserAccounts accounts = getUserAccountsForCaller();
        int userId = Binder.getCallingUserHandle().getIdentifier();
        if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
            return false;
        }
        long identityToken = clearCallingIdentity();
        try {
            return removeAccountInternal(accounts, account);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private voidremoveAccountFromCacheLocked(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account)

        final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
        if (oldAccountsForType != null) {
            ArrayList<Account> newAccountsList = new ArrayList<Account>();
            for (Account curAccount : oldAccountsForType) {
                if (!curAccount.equals(account)) {
                    newAccountsList.add(curAccount);
                }
            }
            if (newAccountsList.isEmpty()) {
                accounts.accountCache.remove(account.type);
            } else {
                Account[] newAccountsForType = new Account[newAccountsList.size()];
                newAccountsForType = newAccountsList.toArray(newAccountsForType);
                accounts.accountCache.put(account.type, newAccountsForType);
            }
        }
        accounts.userDataCache.remove(account);
        accounts.authTokenCache.remove(account);
        accounts.previousNameCache.remove(account);
    
protected voidremoveAccountInternal(android.accounts.Account account)

        removeAccountInternal(getUserAccountsForCaller(), account);
    
private booleanremoveAccountInternal(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account)

        int deleted;
        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
            deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
                    + "=?",
                    new String[]{account.name, account.type});
            removeAccountFromCacheLocked(accounts, account);
            sendAccountsChangedBroadcast(accounts.userId);
        }
        if (accounts.userId == UserHandle.USER_OWNER) {
            // Owner's account was removed, remove from any users that are sharing
            // this account.
            long id = Binder.clearCallingIdentity();
            try {
                List<UserInfo> users = mUserManager.getUsers(true);
                for (UserInfo user : users) {
                    if (!user.isPrimary() && user.isRestricted()) {
                        removeSharedAccountAsUser(account, user.id);
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(id);
            }
        }
        return (deleted > 0);
    
public booleanremoveSharedAccountAsUser(android.accounts.Account account, int userId)

        userId = handleIncomingUser(userId);
        UserAccounts accounts = getUserAccounts(userId);
        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
        int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
                new String[] {account.name, account.type});
        if (r > 0) {
            removeAccountInternal(accounts, account);
        }
        return r > 0;
    
public voidrenameAccount(android.accounts.IAccountManagerResponse response, android.accounts.Account accountToRename, java.lang.String newName)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
                + ", caller's uid " + Binder.getCallingUid()
                + ", pid " + Binder.getCallingPid());
        }
        if (accountToRename == null) throw new IllegalArgumentException("account is null");
        checkAuthenticateAccountsPermission(accountToRename);
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
            Bundle result = new Bundle();
            result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
            result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
            try {
                response.onResult(result);
            } catch (RemoteException e) {
                Log.w(TAG, e.getMessage());
            }
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private android.accounts.AccountrenameAccountInternal(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account accountToRename, java.lang.String newName)

        Account resultAccount = null;
        /*
         * Cancel existing notifications. Let authenticators
         * re-post notifications as required. But we don't know if
         * the authenticators have bound their notifications to
         * now stale account name data.
         *
         * With a rename api, we might not need to do this anymore but it
         * shouldn't hurt.
         */
        cancelNotification(
                getSigninRequiredNotificationId(accounts, accountToRename),
                 new UserHandle(accounts.userId));
        synchronized(accounts.credentialsPermissionNotificationIds) {
            for (Pair<Pair<Account, String>, Integer> pair:
                    accounts.credentialsPermissionNotificationIds.keySet()) {
                if (accountToRename.equals(pair.first.first)) {
                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
                    cancelNotification(id, new UserHandle(accounts.userId));
                }
            }
        }
        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
            db.beginTransaction();
            boolean isSuccessful = false;
            Account renamedAccount = new Account(newName, accountToRename.type);
            try {
                final ContentValues values = new ContentValues();
                values.put(ACCOUNTS_NAME, newName);
                values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
                final long accountId = getAccountIdLocked(db, accountToRename);
                if (accountId >= 0) {
                    final String[] argsAccountId = { String.valueOf(accountId) };
                    db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
                    db.setTransactionSuccessful();
                    isSuccessful = true;
                }
            } finally {
                db.endTransaction();
                if (isSuccessful) {
                    /*
                     * Database transaction was successful. Clean up cached
                     * data associated with the account in the user profile.
                     */
                    insertAccountIntoCacheLocked(accounts, renamedAccount);
                    /*
                     * Extract the data and token caches before removing the
                     * old account to preserve the user data associated with
                     * the account.
                     */
                    HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
                    HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
                    removeAccountFromCacheLocked(accounts, accountToRename);
                    /*
                     * Update the cached data associated with the renamed
                     * account.
                     */
                    accounts.userDataCache.put(renamedAccount, tmpData);
                    accounts.authTokenCache.put(renamedAccount, tmpTokens);
                    accounts.previousNameCache.put(
                          renamedAccount,
                          new AtomicReference<String>(accountToRename.name));
                    resultAccount = renamedAccount;

                    if (accounts.userId == UserHandle.USER_OWNER) {
                        /*
                         * Owner's account was renamed, rename the account for
                         * those users with which the account was shared.
                         */
                        List<UserInfo> users = mUserManager.getUsers(true);
                        for (UserInfo user : users) {
                            if (!user.isPrimary() && user.isRestricted()) {
                                renameSharedAccountAsUser(accountToRename, newName, user.id);
                            }
                        }
                    }
                    sendAccountsChangedBroadcast(accounts.userId);
                }
            }
        }
        return resultAccount;
    
public booleanrenameSharedAccountAsUser(android.accounts.Account account, java.lang.String newName, int userId)

        userId = handleIncomingUser(userId);
        UserAccounts accounts = getUserAccounts(userId);
        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
        final ContentValues values = new ContentValues();
        values.put(ACCOUNTS_NAME, newName);
        values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
        int r = db.update(
                TABLE_SHARED_ACCOUNTS,
                values,
                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
                new String[] { account.name, account.type });
        if (r > 0) {
            // Recursively rename the account.
            renameAccountInternal(accounts, account, newName);
        }
        return r > 0;
    
private voidrevokeAppPermission(android.accounts.Account account, java.lang.String authTokenType, int uid)
Don't allow callers with the given uid permission to get credentials for account/authTokenType.

Although this is public it can only be accessed via the AccountManagerService object which is in the system. This means we don't need to protect it with permissions.

hide

        if (account == null || authTokenType == null) {
            Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
            return;
        }
        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
            db.beginTransaction();
            try {
                long accountId = getAccountIdLocked(db, account);
                if (accountId >= 0) {
                    db.delete(TABLE_GRANTS,
                            GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
                                    + GRANTS_GRANTEE_UID + "=?",
                            new String[]{String.valueOf(accountId), authTokenType,
                                    String.valueOf(uid)});
                    db.setTransactionSuccessful();
                }
            } finally {
                db.endTransaction();
            }
            cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
                    new UserHandle(accounts.userId));
        }
    
private booleansaveAuthTokenToDatabase(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account, java.lang.String type, java.lang.String authToken)

        if (account == null || type == null) {
            return false;
        }
        cancelNotification(getSigninRequiredNotificationId(accounts, account),
                new UserHandle(accounts.userId));
        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
            db.beginTransaction();
            try {
                long accountId = getAccountIdLocked(db, account);
                if (accountId < 0) {
                    return false;
                }
                db.delete(TABLE_AUTHTOKENS,
                        AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
                        new String[]{type});
                ContentValues values = new ContentValues();
                values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
                values.put(AUTHTOKENS_TYPE, type);
                values.put(AUTHTOKENS_AUTHTOKEN, authToken);
                if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
                    db.setTransactionSuccessful();
                    writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
                    return true;
                }
                return false;
            } finally {
                db.endTransaction();
            }
        }
    
private static booleanscanArgs(java.lang.String[] args, java.lang.String value)
Searches array of arguments for the specified string

param
args array of argument strings
param
value value to search for
return
true if the value is contained in the array

        if (args != null) {
            for (String arg : args) {
                if (value.equals(arg)) {
                    return true;
                }
            }
        }
        return false;
    
private voidsendAccountsChangedBroadcast(int userId)

        Log.i(TAG, "the accounts changed, sending broadcast of "
                + ACCOUNTS_CHANGED_INTENT.getAction());
        mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
    
public voidsetAuthToken(android.accounts.Account account, java.lang.String authTokenType, java.lang.String authToken)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "setAuthToken: " + account
                    + ", authTokenType " + authTokenType
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (account == null) throw new IllegalArgumentException("account is null");
        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
        checkAuthenticateAccountsPermission(account);
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public voidsetPassword(android.accounts.Account account, java.lang.String password)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "setAuthToken: " + account
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (account == null) throw new IllegalArgumentException("account is null");
        checkAuthenticateAccountsPermission(account);
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            setPasswordInternal(accounts, account, password);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private voidsetPasswordInternal(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account, java.lang.String password)

        if (account == null) {
            return;
        }
        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
            db.beginTransaction();
            try {
                final ContentValues values = new ContentValues();
                values.put(ACCOUNTS_PASSWORD, password);
                final long accountId = getAccountIdLocked(db, account);
                if (accountId >= 0) {
                    final String[] argsAccountId = {String.valueOf(accountId)};
                    db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
                    db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
                    accounts.authTokenCache.remove(account);
                    db.setTransactionSuccessful();
                }
            } finally {
                db.endTransaction();
            }
            sendAccountsChangedBroadcast(accounts.userId);
        }
    
public voidsetUserData(android.accounts.Account account, java.lang.String key, java.lang.String value)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "setUserData: " + account
                    + ", key " + key
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (key == null) throw new IllegalArgumentException("key is null");
        if (account == null) throw new IllegalArgumentException("account is null");
        checkAuthenticateAccountsPermission(account);
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            setUserdataInternal(accounts, account, key, value);
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private voidsetUserdataInternal(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.accounts.Account account, java.lang.String key, java.lang.String value)

        if (account == null || key == null) {
            return;
        }
        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
            db.beginTransaction();
            try {
                long accountId = getAccountIdLocked(db, account);
                if (accountId < 0) {
                    return;
                }
                long extrasId = getExtrasIdLocked(db, accountId, key);
                if (extrasId < 0 ) {
                    extrasId = insertExtraLocked(db, accountId, key, value);
                    if (extrasId < 0) {
                        return;
                    }
                } else {
                    ContentValues values = new ContentValues();
                    values.put(EXTRAS_VALUE, value);
                    if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
                        return;
                    }

                }
                writeUserDataIntoCacheLocked(accounts, db, account, key, value);
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        }
    
private voidshowCantAddAccount(int errorCode, int userId)

        Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
        cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
        cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        long identityToken = clearCallingIdentity();
        try {
            mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId));
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
private static final java.lang.StringstringArrayToString(java.lang.String[] value)

        return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
    
public voidsystemReady()

    
public voidupdateAppPermission(android.accounts.Account account, java.lang.String authTokenType, int uid, boolean value)

        final int callingUid = getCallingUid();

        if (callingUid != Process.SYSTEM_UID) {
            throw new SecurityException();
        }

        if (value) {
            grantAppPermission(account, authTokenType, uid);
        } else {
            revokeAppPermission(account, authTokenType, uid);
        }
    
public voidupdateCredentials(android.accounts.IAccountManagerResponse response, android.accounts.Account account, java.lang.String authTokenType, boolean expectActivityLaunch, android.os.Bundle loginOptions)

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "updateCredentials: " + account
                    + ", response " + response
                    + ", authTokenType " + authTokenType
                    + ", expectActivityLaunch " + expectActivityLaunch
                    + ", caller's uid " + Binder.getCallingUid()
                    + ", pid " + Binder.getCallingPid());
        }
        if (response == null) throw new IllegalArgumentException("response is null");
        if (account == null) throw new IllegalArgumentException("account is null");
        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
        checkManageAccountsPermission();
        UserAccounts accounts = getUserAccountsForCaller();
        long identityToken = clearCallingIdentity();
        try {
            new Session(accounts, response, account.type, expectActivityLaunch,
                    true /* stripAuthTokenFromResult */) {
                @Override
                public void run() throws RemoteException {
                    mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
                }
                @Override
                protected String toDebugString(long now) {
                    if (loginOptions != null) loginOptions.keySet();
                    return super.toDebugString(now) + ", updateCredentials"
                            + ", " + account
                            + ", authTokenType " + authTokenType
                            + ", loginOptions " + loginOptions;
                }
            }.bind();
        } finally {
            restoreCallingIdentity(identityToken);
        }
    
public voidvalidateAccounts(int userId)
Validate internal set of accounts against installed authenticators for given user. Clears cached authenticators before validating.

        final UserAccounts accounts = getUserAccounts(userId);

        // Invalidate user-specific cache to make sure we catch any
        // removed authenticators.
        validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
    
private voidvalidateAccountsInternal(com.android.server.accounts.AccountManagerService$UserAccounts accounts, boolean invalidateAuthenticatorCache)
Validate internal set of accounts against installed authenticators for given user. Clear cached authenticators before validating when requested.

        if (invalidateAuthenticatorCache) {
            mAuthenticatorCache.invalidateCache(accounts.userId);
        }

        final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet();
        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
                mAuthenticatorCache.getAllServices(accounts.userId)) {
            knownAuth.add(service.type);
        }

        synchronized (accounts.cacheLock) {
            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
            boolean accountDeleted = false;
            Cursor cursor = db.query(TABLE_ACCOUNTS,
                    new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
                    null, null, null, null, ACCOUNTS_ID);
            try {
                accounts.accountCache.clear();
                final HashMap<String, ArrayList<String>> accountNamesByType =
                        new LinkedHashMap<String, ArrayList<String>>();
                while (cursor.moveToNext()) {
                    final long accountId = cursor.getLong(0);
                    final String accountType = cursor.getString(1);
                    final String accountName = cursor.getString(2);

                    if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) {
                        Slog.w(TAG, "deleting account " + accountName + " because type "
                                + accountType + " no longer has a registered authenticator");
                        db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
                        accountDeleted = true;
                        final Account account = new Account(accountName, accountType);
                        accounts.userDataCache.remove(account);
                        accounts.authTokenCache.remove(account);
                    } else {
                        ArrayList<String> accountNames = accountNamesByType.get(accountType);
                        if (accountNames == null) {
                            accountNames = new ArrayList<String>();
                            accountNamesByType.put(accountType, accountNames);
                        }
                        accountNames.add(accountName);
                    }
                }
                for (Map.Entry<String, ArrayList<String>> cur
                        : accountNamesByType.entrySet()) {
                    final String accountType = cur.getKey();
                    final ArrayList<String> accountNames = cur.getValue();
                    final Account[] accountsForType = new Account[accountNames.size()];
                    int i = 0;
                    for (String accountName : accountNames) {
                        accountsForType[i] = new Account(accountName, accountType);
                        ++i;
                    }
                    accounts.accountCache.put(accountType, accountsForType);
                }
            } finally {
                cursor.close();
                if (accountDeleted) {
                    sendAccountsChangedBroadcast(accounts.userId);
                }
            }
        }
    
protected voidwriteAuthTokenIntoCacheLocked(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.database.sqlite.SQLiteDatabase db, android.accounts.Account account, java.lang.String key, java.lang.String value)

        HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
        if (authTokensForAccount == null) {
            authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
            accounts.authTokenCache.put(account, authTokensForAccount);
        }
        if (value == null) {
            authTokensForAccount.remove(key);
        } else {
            authTokensForAccount.put(key, value);
        }
    
protected voidwriteUserDataIntoCacheLocked(com.android.server.accounts.AccountManagerService$UserAccounts accounts, android.database.sqlite.SQLiteDatabase db, android.accounts.Account account, java.lang.String key, java.lang.String value)

        HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
        if (userDataForAccount == null) {
            userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
            accounts.userDataCache.put(account, userDataForAccount);
        }
        if (value == null) {
            userDataForAccount.remove(key);
        } else {
            userDataForAccount.put(key, value);
        }