AccountManagerService.java revision a0a58a2fd5cbc1bb8056227bb04f276b364c6e96
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.accounts;
18
19import android.Manifest;
20import android.accounts.AbstractAccountAuthenticator;
21import android.accounts.Account;
22import android.accounts.AccountAndUser;
23import android.accounts.AccountAuthenticatorResponse;
24import android.accounts.AccountManager;
25import android.accounts.AuthenticatorDescription;
26import android.accounts.CantAddAccountActivity;
27import android.accounts.GrantCredentialsPermissionActivity;
28import android.accounts.IAccountAuthenticator;
29import android.accounts.IAccountAuthenticatorResponse;
30import android.accounts.IAccountManager;
31import android.accounts.IAccountManagerResponse;
32import android.annotation.NonNull;
33import android.app.ActivityManager;
34import android.app.ActivityManagerNative;
35import android.app.AppGlobals;
36import android.app.AppOpsManager;
37import android.app.Notification;
38import android.app.NotificationManager;
39import android.app.PendingIntent;
40import android.app.admin.DeviceAdminInfo;
41import android.app.admin.DevicePolicyManager;
42import android.app.admin.DevicePolicyManagerInternal;
43import android.content.BroadcastReceiver;
44import android.content.ComponentName;
45import android.content.ContentValues;
46import android.content.Context;
47import android.content.Intent;
48import android.content.IntentFilter;
49import android.content.ServiceConnection;
50import android.content.pm.ActivityInfo;
51import android.content.pm.ApplicationInfo;
52import android.content.pm.PackageInfo;
53import android.content.pm.PackageManager;
54import android.content.pm.PackageManager.NameNotFoundException;
55import android.content.pm.RegisteredServicesCache;
56import android.content.pm.RegisteredServicesCacheListener;
57import android.content.pm.ResolveInfo;
58import android.content.pm.Signature;
59import android.content.pm.UserInfo;
60import android.database.Cursor;
61import android.database.DatabaseUtils;
62import android.database.sqlite.SQLiteDatabase;
63import android.database.sqlite.SQLiteOpenHelper;
64import android.database.sqlite.SQLiteStatement;
65import android.os.Binder;
66import android.os.Bundle;
67import android.os.Environment;
68import android.os.Handler;
69import android.os.IBinder;
70import android.os.Looper;
71import android.os.Message;
72import android.os.Parcel;
73import android.os.Process;
74import android.os.RemoteException;
75import android.os.SystemClock;
76import android.os.UserHandle;
77import android.os.UserManager;
78import android.text.TextUtils;
79import android.util.Log;
80import android.util.Pair;
81import android.util.Slog;
82import android.util.SparseArray;
83
84import com.android.internal.R;
85import com.android.internal.util.ArrayUtils;
86import com.android.internal.util.IndentingPrintWriter;
87import com.android.server.FgThread;
88import com.android.server.LocalServices;
89import com.google.android.collect.Lists;
90import com.google.android.collect.Sets;
91
92import java.io.File;
93import java.io.FileDescriptor;
94import java.io.PrintWriter;
95import java.security.GeneralSecurityException;
96import java.security.MessageDigest;
97import java.security.NoSuchAlgorithmException;
98import java.text.SimpleDateFormat;
99import java.util.ArrayList;
100import java.util.Arrays;
101import java.util.Collection;
102import java.util.Date;
103import java.util.HashMap;
104import java.util.HashSet;
105import java.util.Iterator;
106import java.util.LinkedHashMap;
107import java.util.List;
108import java.util.Map;
109import java.util.Map.Entry;
110import java.util.Set;
111import java.util.concurrent.atomic.AtomicInteger;
112import java.util.concurrent.atomic.AtomicReference;
113
114/**
115 * A system service that provides  account, password, and authtoken management for all
116 * accounts on the device. Some of these calls are implemented with the help of the corresponding
117 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
118 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
119 *    AccountManager accountManager = AccountManager.get(context);
120 * @hide
121 */
122public class AccountManagerService
123        extends IAccountManager.Stub
124        implements RegisteredServicesCacheListener<AuthenticatorDescription> {
125
126    private static final String TAG = "AccountManagerService";
127
128    private static final String DATABASE_NAME = "accounts.db";
129    private static final int DATABASE_VERSION = 9;
130
131    private static final int MAX_DEBUG_DB_SIZE = 64;
132
133    private final Context mContext;
134
135    private final PackageManager mPackageManager;
136    private final AppOpsManager mAppOpsManager;
137    private UserManager mUserManager;
138
139    private final MessageHandler mMessageHandler;
140
141    // Messages that can be sent on mHandler
142    private static final int MESSAGE_TIMED_OUT = 3;
143    private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
144
145    private final IAccountAuthenticatorCache mAuthenticatorCache;
146
147    private static final String TABLE_ACCOUNTS = "accounts";
148    private static final String ACCOUNTS_ID = "_id";
149    private static final String ACCOUNTS_NAME = "name";
150    private static final String ACCOUNTS_TYPE = "type";
151    private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
152    private static final String ACCOUNTS_PASSWORD = "password";
153    private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
154    private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
155            "last_password_entry_time_millis_epoch";
156
157    private static final String TABLE_AUTHTOKENS = "authtokens";
158    private static final String AUTHTOKENS_ID = "_id";
159    private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
160    private static final String AUTHTOKENS_TYPE = "type";
161    private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
162
163    private static final String TABLE_GRANTS = "grants";
164    private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
165    private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
166    private static final String GRANTS_GRANTEE_UID = "uid";
167
168    private static final String TABLE_EXTRAS = "extras";
169    private static final String EXTRAS_ID = "_id";
170    private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
171    private static final String EXTRAS_KEY = "key";
172    private static final String EXTRAS_VALUE = "value";
173
174    private static final String TABLE_META = "meta";
175    private static final String META_KEY = "key";
176    private static final String META_VALUE = "value";
177
178    private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
179
180    private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
181            new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
182    private static final Intent ACCOUNTS_CHANGED_INTENT;
183
184    static {
185        ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
186        ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
187    }
188
189    private static final String COUNT_OF_MATCHING_GRANTS = ""
190            + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
191            + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
192            + " AND " + GRANTS_GRANTEE_UID + "=?"
193            + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
194            + " AND " + ACCOUNTS_NAME + "=?"
195            + " AND " + ACCOUNTS_TYPE + "=?";
196
197    private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
198            AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
199
200    private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
201            AUTHTOKENS_AUTHTOKEN};
202
203    private static final String SELECTION_USERDATA_BY_ACCOUNT =
204            EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
205    private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
206
207    private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX =
208            "auth_uid_for_type:";
209    private static final String META_KEY_DELIMITER = ":";
210    private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
211
212    private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
213    private final AtomicInteger mNotificationIds = new AtomicInteger(1);
214
215    static class UserAccounts {
216        private final int userId;
217        private final DatabaseHelper openHelper;
218        private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
219                credentialsPermissionNotificationIds =
220                new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
221        private final HashMap<Account, Integer> signinRequiredNotificationIds =
222                new HashMap<Account, Integer>();
223        private final Object cacheLock = new Object();
224        /** protected by the {@link #cacheLock} */
225        private final HashMap<String, Account[]> accountCache =
226                new LinkedHashMap<String, Account[]>();
227        /** protected by the {@link #cacheLock} */
228        private final HashMap<Account, HashMap<String, String>> userDataCache =
229                new HashMap<Account, HashMap<String, String>>();
230        /** protected by the {@link #cacheLock} */
231        private final HashMap<Account, HashMap<String, String>> authTokenCache =
232                new HashMap<Account, HashMap<String, String>>();
233
234        /** protected by the {@link #cacheLock} */
235        private final TokenCache accountTokenCaches = new TokenCache();
236
237        /**
238         * protected by the {@link #cacheLock}
239         *
240         * Caches the previous names associated with an account. Previous names
241         * should be cached because we expect that when an Account is renamed,
242         * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
243         * want to know if the accounts they care about have been renamed.
244         *
245         * The previous names are wrapped in an {@link AtomicReference} so that
246         * we can distinguish between those accounts with no previous names and
247         * those whose previous names haven't been cached (yet).
248         */
249        private final HashMap<Account, AtomicReference<String>> previousNameCache =
250                new HashMap<Account, AtomicReference<String>>();
251
252        private int debugDbInsertionPoint = -1;
253        private SQLiteStatement statementForLogging;
254
255        UserAccounts(Context context, int userId) {
256            this.userId = userId;
257            synchronized (cacheLock) {
258                openHelper = new DatabaseHelper(context, userId);
259            }
260        }
261    }
262
263    private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
264
265    private static AtomicReference<AccountManagerService> sThis =
266            new AtomicReference<AccountManagerService>();
267    private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
268
269    /**
270     * This should only be called by system code. One should only call this after the service
271     * has started.
272     * @return a reference to the AccountManagerService instance
273     * @hide
274     */
275    public static AccountManagerService getSingleton() {
276        return sThis.get();
277    }
278
279    public AccountManagerService(Context context) {
280        this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
281    }
282
283    public AccountManagerService(Context context, PackageManager packageManager,
284            IAccountAuthenticatorCache authenticatorCache) {
285        mContext = context;
286        mPackageManager = packageManager;
287        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
288
289        mMessageHandler = new MessageHandler(FgThread.get().getLooper());
290
291        mAuthenticatorCache = authenticatorCache;
292        mAuthenticatorCache.setListener(this, null /* Handler */);
293
294        sThis.set(this);
295
296        IntentFilter intentFilter = new IntentFilter();
297        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
298        intentFilter.addDataScheme("package");
299        mContext.registerReceiver(new BroadcastReceiver() {
300            @Override
301            public void onReceive(Context context1, Intent intent) {
302                // Don't delete accounts when updating a authenticator's
303                // package.
304                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
305                    /* Purging data requires file io, don't block the main thread. This is probably
306                     * less than ideal because we are introducing a race condition where old grants
307                     * could be exercised until they are purged. But that race condition existed
308                     * anyway with the broadcast receiver.
309                     *
310                     * Ideally, we would completely clear the cache, purge data from the database,
311                     * and then rebuild the cache. All under the cache lock. But that change is too
312                     * large at this point.
313                     */
314                    Runnable r = new Runnable() {
315                        @Override
316                        public void run() {
317                            purgeOldGrantsAll();
318                        }
319                    };
320                    new Thread(r).start();
321                }
322            }
323        }, intentFilter);
324
325        IntentFilter userFilter = new IntentFilter();
326        userFilter.addAction(Intent.ACTION_USER_REMOVED);
327        userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
328        mContext.registerReceiverAsUser(new BroadcastReceiver() {
329            @Override
330            public void onReceive(Context context, Intent intent) {
331                String action = intent.getAction();
332                if (Intent.ACTION_USER_REMOVED.equals(action)) {
333                    onUserRemoved(intent);
334                } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
335                    onUserUnlocked(intent);
336                }
337            }
338        }, UserHandle.ALL, userFilter, null, null);
339    }
340
341    @Override
342    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
343            throws RemoteException {
344        try {
345            return super.onTransact(code, data, reply, flags);
346        } catch (RuntimeException e) {
347            // The account manager only throws security exceptions, so let's
348            // log all others.
349            if (!(e instanceof SecurityException)) {
350                Slog.wtf(TAG, "Account Manager Crash", e);
351            }
352            throw e;
353        }
354    }
355
356    public void systemReady() {
357    }
358
359    private UserManager getUserManager() {
360        if (mUserManager == null) {
361            mUserManager = UserManager.get(mContext);
362        }
363        return mUserManager;
364    }
365
366    /**
367     * Validate internal set of accounts against installed authenticators for
368     * given user. Clears cached authenticators before validating.
369     */
370    public void validateAccounts(int userId) {
371        final UserAccounts accounts = getUserAccounts(userId);
372
373        // Invalidate user-specific cache to make sure we catch any
374        // removed authenticators.
375        validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
376    }
377
378    /**
379     * Validate internal set of accounts against installed authenticators for
380     * given user. Clear cached authenticators before validating when requested.
381     */
382    private void validateAccountsInternal(
383            UserAccounts accounts, boolean invalidateAuthenticatorCache) {
384        if (invalidateAuthenticatorCache) {
385            mAuthenticatorCache.invalidateCache(accounts.userId);
386        }
387
388        final HashMap<String, Integer> knownAuth = new HashMap<>();
389        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
390                mAuthenticatorCache.getAllServices(accounts.userId)) {
391            knownAuth.put(service.type.type, service.uid);
392        }
393
394        synchronized (accounts.cacheLock) {
395            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
396            boolean accountDeleted = false;
397
398            // Get a list of stored authenticator type and UID
399            Cursor metaCursor = db.query(
400                    TABLE_META,
401                    new String[] {META_KEY, META_VALUE},
402                    SELECTION_META_BY_AUTHENTICATOR_TYPE,
403                    new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
404                    null /* groupBy */,
405                    null /* having */,
406                    META_KEY);
407            // Create a list of authenticator type whose previous uid no longer exists
408            HashSet<String> obsoleteAuthType = Sets.newHashSet();
409            try {
410                while (metaCursor.moveToNext()) {
411                    String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
412                    String uid = metaCursor.getString(1);
413                    if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uid)) {
414                        // Should never happen.
415                        Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
416                                + ", uid empty: " + TextUtils.isEmpty(uid));
417                        continue;
418                    }
419                    Integer knownUid = knownAuth.get(type);
420                    if (knownUid != null && uid.equals(knownUid.toString())) {
421                        // Remove it from the knownAuth list if it's unchanged.
422                        knownAuth.remove(type);
423                    } else {
424                        // Only add it to the list if it no longer exists or uid different
425                        obsoleteAuthType.add(type);
426                        // And delete it from the TABLE_META
427                        db.delete(
428                                TABLE_META,
429                                META_KEY + "=? AND " + META_VALUE + "=?",
430                                new String[] {
431                                        META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
432                                        uid}
433                                );
434                    }
435                }
436            } finally {
437                metaCursor.close();
438            }
439
440            // Add the newly registered authenticator to TABLE_META
441            Iterator<Entry<String, Integer>> iterator = knownAuth.entrySet().iterator();
442            while (iterator.hasNext()) {
443                Entry<String, Integer> entry = iterator.next();
444                ContentValues values = new ContentValues();
445                values.put(META_KEY,
446                        META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
447                values.put(META_VALUE, entry.getValue());
448                db.insert(TABLE_META, null, values);
449            }
450
451            Cursor cursor = db.query(TABLE_ACCOUNTS,
452                    new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
453                    null, null, null, null, ACCOUNTS_ID);
454            try {
455                accounts.accountCache.clear();
456                final HashMap<String, ArrayList<String>> accountNamesByType =
457                        new LinkedHashMap<String, ArrayList<String>>();
458                while (cursor.moveToNext()) {
459                    final long accountId = cursor.getLong(0);
460                    final String accountType = cursor.getString(1);
461                    final String accountName = cursor.getString(2);
462
463                    if (obsoleteAuthType.contains(accountType)) {
464                        Slog.w(TAG, "deleting account " + accountName + " because type "
465                                + accountType + "'s registered authenticator no longer exist.");
466                        db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
467                        accountDeleted = true;
468
469                        logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
470                                accountId, accounts);
471
472                        final Account account = new Account(accountName, accountType);
473                        accounts.userDataCache.remove(account);
474                        accounts.authTokenCache.remove(account);
475                        accounts.accountTokenCaches.remove(account);
476                    } else {
477                        ArrayList<String> accountNames = accountNamesByType.get(accountType);
478                        if (accountNames == null) {
479                            accountNames = new ArrayList<String>();
480                            accountNamesByType.put(accountType, accountNames);
481                        }
482                        accountNames.add(accountName);
483                    }
484                }
485                for (Map.Entry<String, ArrayList<String>> cur
486                        : accountNamesByType.entrySet()) {
487                    final String accountType = cur.getKey();
488                    final ArrayList<String> accountNames = cur.getValue();
489                    final Account[] accountsForType = new Account[accountNames.size()];
490                    int i = 0;
491                    for (String accountName : accountNames) {
492                        accountsForType[i] = new Account(accountName, accountType);
493                        ++i;
494                    }
495                    accounts.accountCache.put(accountType, accountsForType);
496                }
497            } finally {
498                cursor.close();
499                if (accountDeleted) {
500                    sendAccountsChangedBroadcast(accounts.userId);
501                }
502            }
503        }
504    }
505
506    private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
507            Context context,
508            int userId) {
509        AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
510        HashMap<String, Integer> knownAuth = new HashMap<>();
511        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
512                .getAllServices(userId)) {
513            knownAuth.put(service.type.type, service.uid);
514        }
515        return knownAuth;
516    }
517
518    private UserAccounts getUserAccountsForCaller() {
519        return getUserAccounts(UserHandle.getCallingUserId());
520    }
521
522    protected UserAccounts getUserAccounts(int userId) {
523        synchronized (mUsers) {
524            UserAccounts accounts = mUsers.get(userId);
525            if (accounts == null) {
526                accounts = new UserAccounts(mContext, userId);
527                initializeDebugDbSizeAndCompileSqlStatementForLogging(
528                        accounts.openHelper.getWritableDatabase(), accounts);
529                mUsers.append(userId, accounts);
530                purgeOldGrants(accounts);
531                validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
532            }
533            return accounts;
534        }
535    }
536
537    private void purgeOldGrantsAll() {
538        synchronized (mUsers) {
539            for (int i = 0; i < mUsers.size(); i++) {
540                purgeOldGrants(mUsers.valueAt(i));
541            }
542        }
543    }
544
545    private void purgeOldGrants(UserAccounts accounts) {
546        synchronized (accounts.cacheLock) {
547            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
548            final Cursor cursor = db.query(TABLE_GRANTS,
549                    new String[]{GRANTS_GRANTEE_UID},
550                    null, null, GRANTS_GRANTEE_UID, null, null);
551            try {
552                while (cursor.moveToNext()) {
553                    final int uid = cursor.getInt(0);
554                    final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
555                    if (packageExists) {
556                        continue;
557                    }
558                    Log.d(TAG, "deleting grants for UID " + uid
559                            + " because its package is no longer installed");
560                    db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
561                            new String[]{Integer.toString(uid)});
562                }
563            } finally {
564                cursor.close();
565            }
566        }
567    }
568
569    private void onUserRemoved(Intent intent) {
570        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
571        if (userId < 1) return;
572
573        UserAccounts accounts;
574        synchronized (mUsers) {
575            accounts = mUsers.get(userId);
576            mUsers.remove(userId);
577        }
578        if (accounts == null) {
579            File dbFile = new File(getDatabaseName(userId));
580            dbFile.delete();
581            return;
582        }
583
584        synchronized (accounts.cacheLock) {
585            accounts.openHelper.close();
586            File dbFile = new File(getDatabaseName(userId));
587            dbFile.delete();
588        }
589    }
590
591    private void onUserUnlocked(Intent intent) {
592        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
593        if (userId < 1) return;
594
595        // Check if there's a shared account that needs to be created as an account
596        Account[] sharedAccounts = getSharedAccountsAsUser(userId);
597        if (sharedAccounts == null || sharedAccounts.length == 0) return;
598        Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
599        int parentUserId = UserManager.isSplitSystemUser()
600                ? getUserManager().getUserInfo(userId).restrictedProfileParentId
601                : UserHandle.USER_SYSTEM;
602        if (parentUserId < 0) {
603            Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
604            return;
605        }
606        for (Account sa : sharedAccounts) {
607            if (ArrayUtils.contains(accounts, sa)) continue;
608            // Account doesn't exist. Copy it now.
609            copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
610        }
611    }
612
613    @Override
614    public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
615        validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
616    }
617
618    @Override
619    public String getPassword(Account account) {
620        int callingUid = Binder.getCallingUid();
621        if (Log.isLoggable(TAG, Log.VERBOSE)) {
622            Log.v(TAG, "getPassword: " + account
623                    + ", caller's uid " + Binder.getCallingUid()
624                    + ", pid " + Binder.getCallingPid());
625        }
626        if (account == null) throw new IllegalArgumentException("account is null");
627        int userId = UserHandle.getCallingUserId();
628        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
629            String msg = String.format(
630                    "uid %s cannot get secrets for accounts of type: %s",
631                    callingUid,
632                    account.type);
633            throw new SecurityException(msg);
634        }
635        long identityToken = clearCallingIdentity();
636        try {
637            UserAccounts accounts = getUserAccounts(userId);
638            return readPasswordInternal(accounts, account);
639        } finally {
640            restoreCallingIdentity(identityToken);
641        }
642    }
643
644    private String readPasswordInternal(UserAccounts accounts, Account account) {
645        if (account == null) {
646            return null;
647        }
648
649        synchronized (accounts.cacheLock) {
650            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
651            Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
652                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
653                    new String[]{account.name, account.type}, null, null, null);
654            try {
655                if (cursor.moveToNext()) {
656                    return cursor.getString(0);
657                }
658                return null;
659            } finally {
660                cursor.close();
661            }
662        }
663    }
664
665    @Override
666    public String getPreviousName(Account account) {
667        if (Log.isLoggable(TAG, Log.VERBOSE)) {
668            Log.v(TAG, "getPreviousName: " + account
669                    + ", caller's uid " + Binder.getCallingUid()
670                    + ", pid " + Binder.getCallingPid());
671        }
672        if (account == null) throw new IllegalArgumentException("account is null");
673        int userId = UserHandle.getCallingUserId();
674        long identityToken = clearCallingIdentity();
675        try {
676            UserAccounts accounts = getUserAccounts(userId);
677            return readPreviousNameInternal(accounts, account);
678        } finally {
679            restoreCallingIdentity(identityToken);
680        }
681    }
682
683    private String readPreviousNameInternal(UserAccounts accounts, Account account) {
684        if  (account == null) {
685            return null;
686        }
687        synchronized (accounts.cacheLock) {
688            AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
689            if (previousNameRef == null) {
690                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
691                Cursor cursor = db.query(
692                        TABLE_ACCOUNTS,
693                        new String[]{ ACCOUNTS_PREVIOUS_NAME },
694                        ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
695                        new String[] { account.name, account.type },
696                        null,
697                        null,
698                        null);
699                try {
700                    if (cursor.moveToNext()) {
701                        String previousName = cursor.getString(0);
702                        previousNameRef = new AtomicReference<String>(previousName);
703                        accounts.previousNameCache.put(account, previousNameRef);
704                        return previousName;
705                    } else {
706                        return null;
707                    }
708                } finally {
709                    cursor.close();
710                }
711            } else {
712                return previousNameRef.get();
713            }
714        }
715    }
716
717    @Override
718    public String getUserData(Account account, String key) {
719        final int callingUid = Binder.getCallingUid();
720        if (Log.isLoggable(TAG, Log.VERBOSE)) {
721            String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
722                    account, key, callingUid, Binder.getCallingPid());
723            Log.v(TAG, msg);
724        }
725        if (account == null) throw new IllegalArgumentException("account is null");
726        if (key == null) throw new IllegalArgumentException("key is null");
727        int userId = UserHandle.getCallingUserId();
728        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
729            String msg = String.format(
730                    "uid %s cannot get user data for accounts of type: %s",
731                    callingUid,
732                    account.type);
733            throw new SecurityException(msg);
734        }
735        long identityToken = clearCallingIdentity();
736        try {
737            UserAccounts accounts = getUserAccounts(userId);
738            return readUserDataInternal(accounts, account, key);
739        } finally {
740            restoreCallingIdentity(identityToken);
741        }
742    }
743
744    @Override
745    public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
746        int callingUid = Binder.getCallingUid();
747        if (Log.isLoggable(TAG, Log.VERBOSE)) {
748            Log.v(TAG, "getAuthenticatorTypes: "
749                    + "for user id " + userId
750                    + "caller's uid " + callingUid
751                    + ", pid " + Binder.getCallingPid());
752        }
753        // Only allow the system process to read accounts of other users
754        if (isCrossUser(callingUid, userId)) {
755            throw new SecurityException(
756                    String.format(
757                            "User %s tying to get authenticator types for %s" ,
758                            UserHandle.getCallingUserId(),
759                            userId));
760        }
761
762        final long identityToken = clearCallingIdentity();
763        try {
764            return getAuthenticatorTypesInternal(userId);
765
766        } finally {
767            restoreCallingIdentity(identityToken);
768        }
769    }
770
771    /**
772     * Should only be called inside of a clearCallingIdentity block.
773     */
774    private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
775        Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
776                authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
777        AuthenticatorDescription[] types =
778                new AuthenticatorDescription[authenticatorCollection.size()];
779        int i = 0;
780        for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
781                : authenticatorCollection) {
782            types[i] = authenticator.type;
783            i++;
784        }
785        return types;
786    }
787
788
789
790    private boolean isCrossUser(int callingUid, int userId) {
791        return (userId != UserHandle.getCallingUserId()
792                && callingUid != Process.myUid()
793                && mContext.checkCallingOrSelfPermission(
794                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
795                                != PackageManager.PERMISSION_GRANTED);
796    }
797
798    @Override
799    public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
800        final int callingUid = Binder.getCallingUid();
801        if (Log.isLoggable(TAG, Log.VERBOSE)) {
802            Log.v(TAG, "addAccountExplicitly: " + account
803                    + ", caller's uid " + callingUid
804                    + ", pid " + Binder.getCallingPid());
805        }
806        if (account == null) throw new IllegalArgumentException("account is null");
807        int userId = UserHandle.getCallingUserId();
808        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
809            String msg = String.format(
810                    "uid %s cannot explicitly add accounts of type: %s",
811                    callingUid,
812                    account.type);
813            throw new SecurityException(msg);
814        }
815        /*
816         * Child users are not allowed to add accounts. Only the accounts that are
817         * shared by the parent profile can be added to child profile.
818         *
819         * TODO: Only allow accounts that were shared to be added by
820         *     a limited user.
821         */
822
823        // fails if the account already exists
824        long identityToken = clearCallingIdentity();
825        try {
826            UserAccounts accounts = getUserAccounts(userId);
827            return addAccountInternal(accounts, account, password, extras, false, callingUid);
828        } finally {
829            restoreCallingIdentity(identityToken);
830        }
831    }
832
833    @Override
834    public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
835            final int userFrom, int userTo) {
836        int callingUid = Binder.getCallingUid();
837        if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
838            throw new SecurityException("Calling copyAccountToUser requires "
839                    + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
840        }
841        final UserAccounts fromAccounts = getUserAccounts(userFrom);
842        final UserAccounts toAccounts = getUserAccounts(userTo);
843        if (fromAccounts == null || toAccounts == null) {
844            if (response != null) {
845                Bundle result = new Bundle();
846                result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
847                try {
848                    response.onResult(result);
849                } catch (RemoteException e) {
850                    Slog.w(TAG, "Failed to report error back to the client." + e);
851                }
852            }
853            return;
854        }
855
856        Slog.d(TAG, "Copying account " + account.name
857                + " from user " + userFrom + " to user " + userTo);
858        long identityToken = clearCallingIdentity();
859        try {
860            new Session(fromAccounts, response, account.type, false,
861                    false /* stripAuthTokenFromResult */, account.name,
862                    false /* authDetailsRequired */) {
863                @Override
864                protected String toDebugString(long now) {
865                    return super.toDebugString(now) + ", getAccountCredentialsForClone"
866                            + ", " + account.type;
867                }
868
869                @Override
870                public void run() throws RemoteException {
871                    mAuthenticator.getAccountCredentialsForCloning(this, account);
872                }
873
874                @Override
875                public void onResult(Bundle result) {
876                    if (result != null
877                            && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
878                        // Create a Session for the target user and pass in the bundle
879                        completeCloningAccount(response, result, account, toAccounts, userFrom);
880                    } else {
881                        super.onResult(result);
882                    }
883                }
884            }.bind();
885        } finally {
886            restoreCallingIdentity(identityToken);
887        }
888    }
889
890    @Override
891    public boolean accountAuthenticated(final Account account) {
892        final int callingUid = Binder.getCallingUid();
893        if (Log.isLoggable(TAG, Log.VERBOSE)) {
894            String msg = String.format(
895                    "accountAuthenticated( account: %s, callerUid: %s)",
896                    account,
897                    callingUid);
898            Log.v(TAG, msg);
899        }
900        if (account == null) {
901            throw new IllegalArgumentException("account is null");
902        }
903        int userId = UserHandle.getCallingUserId();
904        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
905            String msg = String.format(
906                    "uid %s cannot notify authentication for accounts of type: %s",
907                    callingUid,
908                    account.type);
909            throw new SecurityException(msg);
910        }
911
912        if (!canUserModifyAccounts(userId, callingUid) ||
913                !canUserModifyAccountsForType(userId, account.type, callingUid)) {
914            return false;
915        }
916
917        long identityToken = clearCallingIdentity();
918        try {
919            UserAccounts accounts = getUserAccounts(userId);
920            return updateLastAuthenticatedTime(account);
921        } finally {
922            restoreCallingIdentity(identityToken);
923        }
924    }
925
926    private boolean updateLastAuthenticatedTime(Account account) {
927        final UserAccounts accounts = getUserAccountsForCaller();
928        synchronized (accounts.cacheLock) {
929            final ContentValues values = new ContentValues();
930            values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
931            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
932            int i = db.update(
933                    TABLE_ACCOUNTS,
934                    values,
935                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
936                    new String[] {
937                            account.name, account.type
938                    });
939            if (i > 0) {
940                return true;
941            }
942        }
943        return false;
944    }
945
946    private void completeCloningAccount(IAccountManagerResponse response,
947            final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
948            final int parentUserId){
949        long id = clearCallingIdentity();
950        try {
951            new Session(targetUser, response, account.type, false,
952                    false /* stripAuthTokenFromResult */, account.name,
953                    false /* authDetailsRequired */) {
954                @Override
955                protected String toDebugString(long now) {
956                    return super.toDebugString(now) + ", getAccountCredentialsForClone"
957                            + ", " + account.type;
958                }
959
960                @Override
961                public void run() throws RemoteException {
962                    // Confirm that the owner's account still exists before this step.
963                    UserAccounts owner = getUserAccounts(parentUserId);
964                    synchronized (owner.cacheLock) {
965                        for (Account acc : getAccounts(parentUserId,
966                                mContext.getOpPackageName())) {
967                            if (acc.equals(account)) {
968                                mAuthenticator.addAccountFromCredentials(
969                                        this, account, accountCredentials);
970                                break;
971                            }
972                        }
973                    }
974                }
975
976                @Override
977                public void onResult(Bundle result) {
978                    // TODO: Anything to do if if succedded?
979                    // TODO: If it failed: Show error notification? Should we remove the shadow
980                    // account to avoid retries?
981                    super.onResult(result);
982                }
983
984                @Override
985                public void onError(int errorCode, String errorMessage) {
986                    super.onError(errorCode,  errorMessage);
987                    // TODO: Show error notification to user
988                    // TODO: Should we remove the shadow account so that it doesn't keep trying?
989                }
990
991            }.bind();
992        } finally {
993            restoreCallingIdentity(id);
994        }
995    }
996
997    private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
998            Bundle extras, boolean restricted, int callingUid) {
999        if (account == null) {
1000            return false;
1001        }
1002        synchronized (accounts.cacheLock) {
1003            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1004            db.beginTransaction();
1005            try {
1006                long numMatches = DatabaseUtils.longForQuery(db,
1007                        "select count(*) from " + TABLE_ACCOUNTS
1008                                + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1009                        new String[]{account.name, account.type});
1010                if (numMatches > 0) {
1011                    Log.w(TAG, "insertAccountIntoDatabase: " + account
1012                            + ", skipping since the account already exists");
1013                    return false;
1014                }
1015                ContentValues values = new ContentValues();
1016                values.put(ACCOUNTS_NAME, account.name);
1017                values.put(ACCOUNTS_TYPE, account.type);
1018                values.put(ACCOUNTS_PASSWORD, password);
1019                values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
1020                long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
1021                if (accountId < 0) {
1022                    Log.w(TAG, "insertAccountIntoDatabase: " + account
1023                            + ", skipping the DB insert failed");
1024                    return false;
1025                }
1026                if (extras != null) {
1027                    for (String key : extras.keySet()) {
1028                        final String value = extras.getString(key);
1029                        if (insertExtraLocked(db, accountId, key, value) < 0) {
1030                            Log.w(TAG, "insertAccountIntoDatabase: " + account
1031                                    + ", skipping since insertExtra failed for key " + key);
1032                            return false;
1033                        }
1034                    }
1035                }
1036                db.setTransactionSuccessful();
1037
1038                logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
1039                        accounts, callingUid);
1040
1041                insertAccountIntoCacheLocked(accounts, account);
1042            } finally {
1043                db.endTransaction();
1044            }
1045            sendAccountsChangedBroadcast(accounts.userId);
1046        }
1047        if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1048            addAccountToLinkedRestrictedUsers(account, accounts.userId);
1049        }
1050        return true;
1051    }
1052
1053    /**
1054     * Adds the account to all linked restricted users as shared accounts. If the user is currently
1055     * running, then clone the account too.
1056     * @param account the account to share with limited users
1057     *
1058     */
1059    private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
1060        List<UserInfo> users = getUserManager().getUsers();
1061        for (UserInfo user : users) {
1062            if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
1063                addSharedAccountAsUser(account, user.id);
1064                if (getUserManager().isUserUnlocked(user.id)) {
1065                    mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
1066                            MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
1067                }
1068            }
1069        }
1070    }
1071
1072    private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
1073        ContentValues values = new ContentValues();
1074        values.put(EXTRAS_KEY, key);
1075        values.put(EXTRAS_ACCOUNTS_ID, accountId);
1076        values.put(EXTRAS_VALUE, value);
1077        return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
1078    }
1079
1080    @Override
1081    public void hasFeatures(IAccountManagerResponse response,
1082            Account account, String[] features, String opPackageName) {
1083        int callingUid = Binder.getCallingUid();
1084        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1085            Log.v(TAG, "hasFeatures: " + account
1086                    + ", response " + response
1087                    + ", features " + stringArrayToString(features)
1088                    + ", caller's uid " + callingUid
1089                    + ", pid " + Binder.getCallingPid());
1090        }
1091        if (response == null) throw new IllegalArgumentException("response is null");
1092        if (account == null) throw new IllegalArgumentException("account is null");
1093        if (features == null) throw new IllegalArgumentException("features is null");
1094        int userId = UserHandle.getCallingUserId();
1095        checkReadAccountsPermitted(callingUid, account.type, userId,
1096                opPackageName);
1097
1098        long identityToken = clearCallingIdentity();
1099        try {
1100            UserAccounts accounts = getUserAccounts(userId);
1101            new TestFeaturesSession(accounts, response, account, features).bind();
1102        } finally {
1103            restoreCallingIdentity(identityToken);
1104        }
1105    }
1106
1107    private class TestFeaturesSession extends Session {
1108        private final String[] mFeatures;
1109        private final Account mAccount;
1110
1111        public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
1112                Account account, String[] features) {
1113            super(accounts, response, account.type, false /* expectActivityLaunch */,
1114                    true /* stripAuthTokenFromResult */, account.name,
1115                    false /* authDetailsRequired */);
1116            mFeatures = features;
1117            mAccount = account;
1118        }
1119
1120        @Override
1121        public void run() throws RemoteException {
1122            try {
1123                mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1124            } catch (RemoteException e) {
1125                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1126            }
1127        }
1128
1129        @Override
1130        public void onResult(Bundle result) {
1131            IAccountManagerResponse response = getResponseAndClose();
1132            if (response != null) {
1133                try {
1134                    if (result == null) {
1135                        response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
1136                        return;
1137                    }
1138                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
1139                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1140                                + response);
1141                    }
1142                    final Bundle newResult = new Bundle();
1143                    newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1144                            result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1145                    response.onResult(newResult);
1146                } catch (RemoteException e) {
1147                    // if the caller is dead then there is no one to care about remote exceptions
1148                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
1149                        Log.v(TAG, "failure while notifying response", e);
1150                    }
1151                }
1152            }
1153        }
1154
1155        @Override
1156        protected String toDebugString(long now) {
1157            return super.toDebugString(now) + ", hasFeatures"
1158                    + ", " + mAccount
1159                    + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1160        }
1161    }
1162
1163    @Override
1164    public void renameAccount(
1165            IAccountManagerResponse response, Account accountToRename, String newName) {
1166        final int callingUid = Binder.getCallingUid();
1167        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1168            Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
1169                + ", caller's uid " + callingUid
1170                + ", pid " + Binder.getCallingPid());
1171        }
1172        if (accountToRename == null) throw new IllegalArgumentException("account is null");
1173        int userId = UserHandle.getCallingUserId();
1174        if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
1175            String msg = String.format(
1176                    "uid %s cannot rename accounts of type: %s",
1177                    callingUid,
1178                    accountToRename.type);
1179            throw new SecurityException(msg);
1180        }
1181        long identityToken = clearCallingIdentity();
1182        try {
1183            UserAccounts accounts = getUserAccounts(userId);
1184            Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
1185            Bundle result = new Bundle();
1186            result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1187            result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1188            try {
1189                response.onResult(result);
1190            } catch (RemoteException e) {
1191                Log.w(TAG, e.getMessage());
1192            }
1193        } finally {
1194            restoreCallingIdentity(identityToken);
1195        }
1196    }
1197
1198    private Account renameAccountInternal(
1199            UserAccounts accounts, Account accountToRename, String newName) {
1200        Account resultAccount = null;
1201        /*
1202         * Cancel existing notifications. Let authenticators
1203         * re-post notifications as required. But we don't know if
1204         * the authenticators have bound their notifications to
1205         * now stale account name data.
1206         *
1207         * With a rename api, we might not need to do this anymore but it
1208         * shouldn't hurt.
1209         */
1210        cancelNotification(
1211                getSigninRequiredNotificationId(accounts, accountToRename),
1212                 new UserHandle(accounts.userId));
1213        synchronized(accounts.credentialsPermissionNotificationIds) {
1214            for (Pair<Pair<Account, String>, Integer> pair:
1215                    accounts.credentialsPermissionNotificationIds.keySet()) {
1216                if (accountToRename.equals(pair.first.first)) {
1217                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
1218                    cancelNotification(id, new UserHandle(accounts.userId));
1219                }
1220            }
1221        }
1222        synchronized (accounts.cacheLock) {
1223            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1224            db.beginTransaction();
1225            boolean isSuccessful = false;
1226            Account renamedAccount = new Account(newName, accountToRename.type);
1227            try {
1228                final ContentValues values = new ContentValues();
1229                values.put(ACCOUNTS_NAME, newName);
1230                values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
1231                final long accountId = getAccountIdLocked(db, accountToRename);
1232                if (accountId >= 0) {
1233                    final String[] argsAccountId = { String.valueOf(accountId) };
1234                    db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1235                    db.setTransactionSuccessful();
1236                    isSuccessful = true;
1237                    logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
1238                            accounts);
1239                }
1240            } finally {
1241                db.endTransaction();
1242                if (isSuccessful) {
1243                    /*
1244                     * Database transaction was successful. Clean up cached
1245                     * data associated with the account in the user profile.
1246                     */
1247                    insertAccountIntoCacheLocked(accounts, renamedAccount);
1248                    /*
1249                     * Extract the data and token caches before removing the
1250                     * old account to preserve the user data associated with
1251                     * the account.
1252                     */
1253                    HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1254                    HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
1255                    removeAccountFromCacheLocked(accounts, accountToRename);
1256                    /*
1257                     * Update the cached data associated with the renamed
1258                     * account.
1259                     */
1260                    accounts.userDataCache.put(renamedAccount, tmpData);
1261                    accounts.authTokenCache.put(renamedAccount, tmpTokens);
1262                    accounts.previousNameCache.put(
1263                          renamedAccount,
1264                          new AtomicReference<String>(accountToRename.name));
1265                    resultAccount = renamedAccount;
1266
1267                    int parentUserId = accounts.userId;
1268                    if (canHaveProfile(parentUserId)) {
1269                        /*
1270                         * Owner or system user account was renamed, rename the account for
1271                         * those users with which the account was shared.
1272                         */
1273                        List<UserInfo> users = getUserManager().getUsers(true);
1274                        for (UserInfo user : users) {
1275                            if (user.isRestricted()
1276                                    && (user.restrictedProfileParentId == parentUserId)) {
1277                                renameSharedAccountAsUser(accountToRename, newName, user.id);
1278                            }
1279                        }
1280                    }
1281                    sendAccountsChangedBroadcast(accounts.userId);
1282                }
1283            }
1284        }
1285        return resultAccount;
1286    }
1287
1288    private boolean canHaveProfile(final int parentUserId) {
1289        final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
1290        return userInfo != null && userInfo.canHaveProfile();
1291    }
1292
1293    @Override
1294    public void removeAccount(IAccountManagerResponse response, Account account,
1295            boolean expectActivityLaunch) {
1296        removeAccountAsUser(
1297                response,
1298                account,
1299                expectActivityLaunch,
1300                UserHandle.getCallingUserId());
1301    }
1302
1303    @Override
1304    public void removeAccountAsUser(IAccountManagerResponse response, Account account,
1305            boolean expectActivityLaunch, int userId) {
1306        final int callingUid = Binder.getCallingUid();
1307        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1308            Log.v(TAG, "removeAccount: " + account
1309                    + ", response " + response
1310                    + ", caller's uid " + callingUid
1311                    + ", pid " + Binder.getCallingPid()
1312                    + ", for user id " + userId);
1313        }
1314        if (response == null) throw new IllegalArgumentException("response is null");
1315        if (account == null) throw new IllegalArgumentException("account is null");
1316        // Only allow the system process to modify accounts of other users
1317        if (isCrossUser(callingUid, userId)) {
1318            throw new SecurityException(
1319                    String.format(
1320                            "User %s tying remove account for %s" ,
1321                            UserHandle.getCallingUserId(),
1322                            userId));
1323        }
1324        /*
1325         * Only the system or authenticator should be allowed to remove accounts for that
1326         * authenticator.  This will let users remove accounts (via Settings in the system) but not
1327         * arbitrary applications (like competing authenticators).
1328         */
1329        UserHandle user = new UserHandle(userId);
1330        if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
1331                && !isSystemUid(callingUid)) {
1332            String msg = String.format(
1333                    "uid %s cannot remove accounts of type: %s",
1334                    callingUid,
1335                    account.type);
1336            throw new SecurityException(msg);
1337        }
1338        if (!canUserModifyAccounts(userId, callingUid)) {
1339            try {
1340                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1341                        "User cannot modify accounts");
1342            } catch (RemoteException re) {
1343            }
1344            return;
1345        }
1346        if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
1347            try {
1348                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1349                        "User cannot modify accounts of this type (policy).");
1350            } catch (RemoteException re) {
1351            }
1352            return;
1353        }
1354        long identityToken = clearCallingIdentity();
1355        UserAccounts accounts = getUserAccounts(userId);
1356        cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
1357        synchronized(accounts.credentialsPermissionNotificationIds) {
1358            for (Pair<Pair<Account, String>, Integer> pair:
1359                accounts.credentialsPermissionNotificationIds.keySet()) {
1360                if (account.equals(pair.first.first)) {
1361                    int id = accounts.credentialsPermissionNotificationIds.get(pair);
1362                    cancelNotification(id, user);
1363                }
1364            }
1365        }
1366
1367        logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1368
1369        try {
1370            new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
1371        } finally {
1372            restoreCallingIdentity(identityToken);
1373        }
1374    }
1375
1376    @Override
1377    public boolean removeAccountExplicitly(Account account) {
1378        final int callingUid = Binder.getCallingUid();
1379        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1380            Log.v(TAG, "removeAccountExplicitly: " + account
1381                    + ", caller's uid " + callingUid
1382                    + ", pid " + Binder.getCallingPid());
1383        }
1384        int userId = Binder.getCallingUserHandle().getIdentifier();
1385        if (account == null) {
1386            /*
1387             * Null accounts should result in returning false, as per
1388             * AccountManage.addAccountExplicitly(...) java doc.
1389             */
1390            Log.e(TAG, "account is null");
1391            return false;
1392        } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1393            String msg = String.format(
1394                    "uid %s cannot explicitly add accounts of type: %s",
1395                    callingUid,
1396                    account.type);
1397            throw new SecurityException(msg);
1398        }
1399        UserAccounts accounts = getUserAccountsForCaller();
1400        logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1401        long identityToken = clearCallingIdentity();
1402        try {
1403            return removeAccountInternal(accounts, account, callingUid);
1404        } finally {
1405            restoreCallingIdentity(identityToken);
1406        }
1407    }
1408
1409    private class RemoveAccountSession extends Session {
1410        final Account mAccount;
1411        public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
1412                Account account, boolean expectActivityLaunch) {
1413            super(accounts, response, account.type, expectActivityLaunch,
1414                    true /* stripAuthTokenFromResult */, account.name,
1415                    false /* authDetailsRequired */);
1416            mAccount = account;
1417        }
1418
1419        @Override
1420        protected String toDebugString(long now) {
1421            return super.toDebugString(now) + ", removeAccount"
1422                    + ", account " + mAccount;
1423        }
1424
1425        @Override
1426        public void run() throws RemoteException {
1427            mAuthenticator.getAccountRemovalAllowed(this, mAccount);
1428        }
1429
1430        @Override
1431        public void onResult(Bundle result) {
1432            if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
1433                    && !result.containsKey(AccountManager.KEY_INTENT)) {
1434                final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
1435                if (removalAllowed) {
1436                    removeAccountInternal(mAccounts, mAccount, getCallingUid());
1437                }
1438                IAccountManagerResponse response = getResponseAndClose();
1439                if (response != null) {
1440                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
1441                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1442                                + response);
1443                    }
1444                    Bundle result2 = new Bundle();
1445                    result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
1446                    try {
1447                        response.onResult(result2);
1448                    } catch (RemoteException e) {
1449                        // ignore
1450                    }
1451                }
1452            }
1453            super.onResult(result);
1454        }
1455    }
1456
1457    /* For testing */
1458    protected void removeAccountInternal(Account account) {
1459        removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
1460    }
1461
1462    private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
1463        int deleted;
1464        synchronized (accounts.cacheLock) {
1465            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1466            final long accountId = getAccountIdLocked(db, account);
1467            deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
1468                    + "=?",
1469                    new String[]{account.name, account.type});
1470            removeAccountFromCacheLocked(accounts, account);
1471            sendAccountsChangedBroadcast(accounts.userId);
1472
1473            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_ACCOUNTS, accountId, accounts);
1474        }
1475        long id = Binder.clearCallingIdentity();
1476        try {
1477            int parentUserId = accounts.userId;
1478            if (canHaveProfile(parentUserId)) {
1479                // Remove from any restricted profiles that are sharing this account.
1480                List<UserInfo> users = getUserManager().getUsers(true);
1481                for (UserInfo user : users) {
1482                    if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
1483                        removeSharedAccountAsUser(account, user.id, callingUid);
1484                    }
1485                }
1486            }
1487        } finally {
1488            Binder.restoreCallingIdentity(id);
1489        }
1490        return (deleted > 0);
1491    }
1492
1493    @Override
1494    public void invalidateAuthToken(String accountType, String authToken) {
1495        int callerUid = Binder.getCallingUid();
1496        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1497            Log.v(TAG, "invalidateAuthToken: accountType " + accountType
1498                    + ", caller's uid " + callerUid
1499                    + ", pid " + Binder.getCallingPid());
1500        }
1501        if (accountType == null) throw new IllegalArgumentException("accountType is null");
1502        if (authToken == null) throw new IllegalArgumentException("authToken is null");
1503        int userId = UserHandle.getCallingUserId();
1504        long identityToken = clearCallingIdentity();
1505        try {
1506            UserAccounts accounts = getUserAccounts(userId);
1507            synchronized (accounts.cacheLock) {
1508                final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1509                db.beginTransaction();
1510                try {
1511                    invalidateAuthTokenLocked(accounts, db, accountType, authToken);
1512                    invalidateCustomTokenLocked(accounts, accountType, authToken);
1513                    db.setTransactionSuccessful();
1514                } finally {
1515                    db.endTransaction();
1516                }
1517            }
1518        } finally {
1519            restoreCallingIdentity(identityToken);
1520        }
1521    }
1522
1523    private void invalidateCustomTokenLocked(
1524            UserAccounts accounts,
1525            String accountType,
1526            String authToken) {
1527        if (authToken == null || accountType == null) {
1528            return;
1529        }
1530        // Also wipe out cached token in memory.
1531        accounts.accountTokenCaches.remove(accountType, authToken);
1532    }
1533
1534    private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
1535            String accountType, String authToken) {
1536        if (authToken == null || accountType == null) {
1537            return;
1538        }
1539        Cursor cursor = db.rawQuery(
1540                "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
1541                        + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
1542                        + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
1543                        + " FROM " + TABLE_ACCOUNTS
1544                        + " JOIN " + TABLE_AUTHTOKENS
1545                        + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
1546                        + " = " + AUTHTOKENS_ACCOUNTS_ID
1547                        + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
1548                        + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
1549                new String[]{authToken, accountType});
1550        try {
1551            while (cursor.moveToNext()) {
1552                long authTokenId = cursor.getLong(0);
1553                String accountName = cursor.getString(1);
1554                String authTokenType = cursor.getString(2);
1555                db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
1556                writeAuthTokenIntoCacheLocked(
1557                        accounts,
1558                        db,
1559                        new Account(accountName, accountType),
1560                        authTokenType,
1561                        null);
1562            }
1563        } finally {
1564            cursor.close();
1565        }
1566    }
1567
1568    private void saveCachedToken(
1569            UserAccounts accounts,
1570            Account account,
1571            String callerPkg,
1572            byte[] callerSigDigest,
1573            String tokenType,
1574            String token,
1575            long expiryMillis) {
1576
1577        if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
1578            return;
1579        }
1580        cancelNotification(getSigninRequiredNotificationId(accounts, account),
1581                new UserHandle(accounts.userId));
1582        synchronized (accounts.cacheLock) {
1583            accounts.accountTokenCaches.put(
1584                    account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
1585        }
1586    }
1587
1588    private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
1589            String authToken) {
1590        if (account == null || type == null) {
1591            return false;
1592        }
1593        cancelNotification(getSigninRequiredNotificationId(accounts, account),
1594                new UserHandle(accounts.userId));
1595        synchronized (accounts.cacheLock) {
1596            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1597            db.beginTransaction();
1598            try {
1599                long accountId = getAccountIdLocked(db, account);
1600                if (accountId < 0) {
1601                    return false;
1602                }
1603                db.delete(TABLE_AUTHTOKENS,
1604                        AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
1605                        new String[]{type});
1606                ContentValues values = new ContentValues();
1607                values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
1608                values.put(AUTHTOKENS_TYPE, type);
1609                values.put(AUTHTOKENS_AUTHTOKEN, authToken);
1610                if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
1611                    db.setTransactionSuccessful();
1612                    writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
1613                    return true;
1614                }
1615                return false;
1616            } finally {
1617                db.endTransaction();
1618            }
1619        }
1620    }
1621
1622    @Override
1623    public String peekAuthToken(Account account, String authTokenType) {
1624        final int callingUid = Binder.getCallingUid();
1625        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1626            Log.v(TAG, "peekAuthToken: " + account
1627                    + ", authTokenType " + authTokenType
1628                    + ", caller's uid " + callingUid
1629                    + ", pid " + Binder.getCallingPid());
1630        }
1631        if (account == null) throw new IllegalArgumentException("account is null");
1632        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1633        int userId = UserHandle.getCallingUserId();
1634        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1635            String msg = String.format(
1636                    "uid %s cannot peek the authtokens associated with accounts of type: %s",
1637                    callingUid,
1638                    account.type);
1639            throw new SecurityException(msg);
1640        }
1641        long identityToken = clearCallingIdentity();
1642        try {
1643            UserAccounts accounts = getUserAccounts(userId);
1644            return readAuthTokenInternal(accounts, account, authTokenType);
1645        } finally {
1646            restoreCallingIdentity(identityToken);
1647        }
1648    }
1649
1650    @Override
1651    public void setAuthToken(Account account, String authTokenType, String authToken) {
1652        final int callingUid = Binder.getCallingUid();
1653        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1654            Log.v(TAG, "setAuthToken: " + account
1655                    + ", authTokenType " + authTokenType
1656                    + ", caller's uid " + callingUid
1657                    + ", pid " + Binder.getCallingPid());
1658        }
1659        if (account == null) throw new IllegalArgumentException("account is null");
1660        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1661        int userId = UserHandle.getCallingUserId();
1662        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1663            String msg = String.format(
1664                    "uid %s cannot set auth tokens associated with accounts of type: %s",
1665                    callingUid,
1666                    account.type);
1667            throw new SecurityException(msg);
1668        }
1669        long identityToken = clearCallingIdentity();
1670        try {
1671            UserAccounts accounts = getUserAccounts(userId);
1672            saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
1673        } finally {
1674            restoreCallingIdentity(identityToken);
1675        }
1676    }
1677
1678    @Override
1679    public void setPassword(Account account, String password) {
1680        final int callingUid = Binder.getCallingUid();
1681        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1682            Log.v(TAG, "setAuthToken: " + account
1683                    + ", caller's uid " + callingUid
1684                    + ", pid " + Binder.getCallingPid());
1685        }
1686        if (account == null) throw new IllegalArgumentException("account is null");
1687        int userId = UserHandle.getCallingUserId();
1688        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1689            String msg = String.format(
1690                    "uid %s cannot set secrets for accounts of type: %s",
1691                    callingUid,
1692                    account.type);
1693            throw new SecurityException(msg);
1694        }
1695        long identityToken = clearCallingIdentity();
1696        try {
1697            UserAccounts accounts = getUserAccounts(userId);
1698            setPasswordInternal(accounts, account, password, callingUid);
1699        } finally {
1700            restoreCallingIdentity(identityToken);
1701        }
1702    }
1703
1704    private void setPasswordInternal(UserAccounts accounts, Account account, String password,
1705            int callingUid) {
1706        if (account == null) {
1707            return;
1708        }
1709        synchronized (accounts.cacheLock) {
1710            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1711            db.beginTransaction();
1712            try {
1713                final ContentValues values = new ContentValues();
1714                values.put(ACCOUNTS_PASSWORD, password);
1715                final long accountId = getAccountIdLocked(db, account);
1716                if (accountId >= 0) {
1717                    final String[] argsAccountId = {String.valueOf(accountId)};
1718                    db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1719                    db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
1720                    accounts.authTokenCache.remove(account);
1721                    accounts.accountTokenCaches.remove(account);
1722                    db.setTransactionSuccessful();
1723
1724                    String action = (password == null || password.length() == 0) ?
1725                            DebugDbHelper.ACTION_CLEAR_PASSWORD
1726                            : DebugDbHelper.ACTION_SET_PASSWORD;
1727                    logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
1728                }
1729            } finally {
1730                db.endTransaction();
1731            }
1732            sendAccountsChangedBroadcast(accounts.userId);
1733        }
1734    }
1735
1736    private void sendAccountsChangedBroadcast(int userId) {
1737        Log.i(TAG, "the accounts changed, sending broadcast of "
1738                + ACCOUNTS_CHANGED_INTENT.getAction());
1739        mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
1740    }
1741
1742    @Override
1743    public void clearPassword(Account account) {
1744        final int callingUid = Binder.getCallingUid();
1745        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1746            Log.v(TAG, "clearPassword: " + account
1747                    + ", caller's uid " + callingUid
1748                    + ", pid " + Binder.getCallingPid());
1749        }
1750        if (account == null) throw new IllegalArgumentException("account is null");
1751        int userId = UserHandle.getCallingUserId();
1752        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1753            String msg = String.format(
1754                    "uid %s cannot clear passwords for accounts of type: %s",
1755                    callingUid,
1756                    account.type);
1757            throw new SecurityException(msg);
1758        }
1759        long identityToken = clearCallingIdentity();
1760        try {
1761            UserAccounts accounts = getUserAccounts(userId);
1762            setPasswordInternal(accounts, account, null, callingUid);
1763        } finally {
1764            restoreCallingIdentity(identityToken);
1765        }
1766    }
1767
1768    @Override
1769    public void setUserData(Account account, String key, String value) {
1770        final int callingUid = Binder.getCallingUid();
1771        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1772            Log.v(TAG, "setUserData: " + account
1773                    + ", key " + key
1774                    + ", caller's uid " + callingUid
1775                    + ", pid " + Binder.getCallingPid());
1776        }
1777        if (key == null) throw new IllegalArgumentException("key is null");
1778        if (account == null) throw new IllegalArgumentException("account is null");
1779        int userId = UserHandle.getCallingUserId();
1780        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1781            String msg = String.format(
1782                    "uid %s cannot set user data for accounts of type: %s",
1783                    callingUid,
1784                    account.type);
1785            throw new SecurityException(msg);
1786        }
1787        long identityToken = clearCallingIdentity();
1788        try {
1789            UserAccounts accounts = getUserAccounts(userId);
1790            setUserdataInternal(accounts, account, key, value);
1791        } finally {
1792            restoreCallingIdentity(identityToken);
1793        }
1794    }
1795
1796    private void setUserdataInternal(UserAccounts accounts, Account account, String key,
1797            String value) {
1798        if (account == null || key == null) {
1799            return;
1800        }
1801        synchronized (accounts.cacheLock) {
1802            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1803            db.beginTransaction();
1804            try {
1805                long accountId = getAccountIdLocked(db, account);
1806                if (accountId < 0) {
1807                    return;
1808                }
1809                long extrasId = getExtrasIdLocked(db, accountId, key);
1810                if (extrasId < 0 ) {
1811                    extrasId = insertExtraLocked(db, accountId, key, value);
1812                    if (extrasId < 0) {
1813                        return;
1814                    }
1815                } else {
1816                    ContentValues values = new ContentValues();
1817                    values.put(EXTRAS_VALUE, value);
1818                    if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
1819                        return;
1820                    }
1821
1822                }
1823                writeUserDataIntoCacheLocked(accounts, db, account, key, value);
1824                db.setTransactionSuccessful();
1825            } finally {
1826                db.endTransaction();
1827            }
1828        }
1829    }
1830
1831    private void onResult(IAccountManagerResponse response, Bundle result) {
1832        if (result == null) {
1833            Log.e(TAG, "the result is unexpectedly null", new Exception());
1834        }
1835        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1836            Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1837                    + response);
1838        }
1839        try {
1840            response.onResult(result);
1841        } catch (RemoteException e) {
1842            // if the caller is dead then there is no one to care about remote
1843            // exceptions
1844            if (Log.isLoggable(TAG, Log.VERBOSE)) {
1845                Log.v(TAG, "failure while notifying response", e);
1846            }
1847        }
1848    }
1849
1850    @Override
1851    public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
1852                                  final String authTokenType)
1853            throws RemoteException {
1854        if (accountType == null) throw new IllegalArgumentException("accountType is null");
1855        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1856
1857        final int callingUid = getCallingUid();
1858        clearCallingIdentity();
1859        if (callingUid != Process.SYSTEM_UID) {
1860            throw new SecurityException("can only call from system");
1861        }
1862        int userId = UserHandle.getUserId(callingUid);
1863        long identityToken = clearCallingIdentity();
1864        try {
1865            UserAccounts accounts = getUserAccounts(userId);
1866            new Session(accounts, response, accountType, false /* expectActivityLaunch */,
1867                    false /* stripAuthTokenFromResult */,  null /* accountName */,
1868                    false /* authDetailsRequired */) {
1869                @Override
1870                protected String toDebugString(long now) {
1871                    return super.toDebugString(now) + ", getAuthTokenLabel"
1872                            + ", " + accountType
1873                            + ", authTokenType " + authTokenType;
1874                }
1875
1876                @Override
1877                public void run() throws RemoteException {
1878                    mAuthenticator.getAuthTokenLabel(this, authTokenType);
1879                }
1880
1881                @Override
1882                public void onResult(Bundle result) {
1883                    if (result != null) {
1884                        String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
1885                        Bundle bundle = new Bundle();
1886                        bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
1887                        super.onResult(bundle);
1888                        return;
1889                    } else {
1890                        super.onResult(result);
1891                    }
1892                }
1893            }.bind();
1894        } finally {
1895            restoreCallingIdentity(identityToken);
1896        }
1897    }
1898
1899    @Override
1900    public void getAuthToken(
1901            IAccountManagerResponse response,
1902            final Account account,
1903            final String authTokenType,
1904            final boolean notifyOnAuthFailure,
1905            final boolean expectActivityLaunch,
1906            final Bundle loginOptions) {
1907        if (Log.isLoggable(TAG, Log.VERBOSE)) {
1908            Log.v(TAG, "getAuthToken: " + account
1909                    + ", response " + response
1910                    + ", authTokenType " + authTokenType
1911                    + ", notifyOnAuthFailure " + notifyOnAuthFailure
1912                    + ", expectActivityLaunch " + expectActivityLaunch
1913                    + ", caller's uid " + Binder.getCallingUid()
1914                    + ", pid " + Binder.getCallingPid());
1915        }
1916        if (response == null) throw new IllegalArgumentException("response is null");
1917        try {
1918            if (account == null) {
1919                Slog.w(TAG, "getAuthToken called with null account");
1920                response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
1921                return;
1922            }
1923            if (authTokenType == null) {
1924                Slog.w(TAG, "getAuthToken called with null authTokenType");
1925                response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
1926                return;
1927            }
1928        } catch (RemoteException e) {
1929            Slog.w(TAG, "Failed to report error back to the client." + e);
1930            return;
1931        }
1932        int userId = UserHandle.getCallingUserId();
1933        long ident = Binder.clearCallingIdentity();
1934        final UserAccounts accounts;
1935        final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
1936        try {
1937            accounts = getUserAccounts(userId);
1938            authenticatorInfo = mAuthenticatorCache.getServiceInfo(
1939                    AuthenticatorDescription.newKey(account.type), accounts.userId);
1940        } finally {
1941            Binder.restoreCallingIdentity(ident);
1942        }
1943
1944        final boolean customTokens =
1945                authenticatorInfo != null && authenticatorInfo.type.customTokens;
1946
1947        // skip the check if customTokens
1948        final int callerUid = Binder.getCallingUid();
1949        final boolean permissionGranted =
1950                customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
1951
1952        // Get the calling package. We will use it for the purpose of caching.
1953        final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
1954        List<String> callerOwnedPackageNames;
1955        ident = Binder.clearCallingIdentity();
1956        try {
1957            callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
1958        } finally {
1959            Binder.restoreCallingIdentity(ident);
1960        }
1961        if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
1962            String msg = String.format(
1963                    "Uid %s is attempting to illegally masquerade as package %s!",
1964                    callerUid,
1965                    callerPkg);
1966            throw new SecurityException(msg);
1967        }
1968
1969        // let authenticator know the identity of the caller
1970        loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
1971        loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
1972
1973        if (notifyOnAuthFailure) {
1974            loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
1975        }
1976
1977        long identityToken = clearCallingIdentity();
1978        try {
1979            // Distill the caller's package signatures into a single digest.
1980            final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
1981
1982            // if the caller has permission, do the peek. otherwise go the more expensive
1983            // route of starting a Session
1984            if (!customTokens && permissionGranted) {
1985                String authToken = readAuthTokenInternal(accounts, account, authTokenType);
1986                if (authToken != null) {
1987                    Bundle result = new Bundle();
1988                    result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
1989                    result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1990                    result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
1991                    onResult(response, result);
1992                    return;
1993                }
1994            }
1995
1996            if (customTokens) {
1997                /*
1998                 * Look up tokens in the new cache only if the loginOptions don't have parameters
1999                 * outside of those expected to be injected by the AccountManager, e.g.
2000                 * ANDORID_PACKAGE_NAME.
2001                 */
2002                String token = readCachedTokenInternal(
2003                        accounts,
2004                        account,
2005                        authTokenType,
2006                        callerPkg,
2007                        callerPkgSigDigest);
2008                if (token != null) {
2009                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
2010                        Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2011                    }
2012                    Bundle result = new Bundle();
2013                    result.putString(AccountManager.KEY_AUTHTOKEN, token);
2014                    result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2015                    result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2016                    onResult(response, result);
2017                    return;
2018                }
2019            }
2020
2021            new Session(accounts, response, account.type, expectActivityLaunch,
2022                    false /* stripAuthTokenFromResult */, account.name,
2023                    false /* authDetailsRequired */) {
2024                @Override
2025                protected String toDebugString(long now) {
2026                    if (loginOptions != null) loginOptions.keySet();
2027                    return super.toDebugString(now) + ", getAuthToken"
2028                            + ", " + account
2029                            + ", authTokenType " + authTokenType
2030                            + ", loginOptions " + loginOptions
2031                            + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2032                }
2033
2034                @Override
2035                public void run() throws RemoteException {
2036                    // If the caller doesn't have permission then create and return the
2037                    // "grant permission" intent instead of the "getAuthToken" intent.
2038                    if (!permissionGranted) {
2039                        mAuthenticator.getAuthTokenLabel(this, authTokenType);
2040                    } else {
2041                        mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2042                    }
2043                }
2044
2045                @Override
2046                public void onResult(Bundle result) {
2047                    if (result != null) {
2048                        if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
2049                            Intent intent = newGrantCredentialsPermissionIntent(
2050                                    account,
2051                                    callerUid,
2052                                    new AccountAuthenticatorResponse(this),
2053                                    authTokenType);
2054                            Bundle bundle = new Bundle();
2055                            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
2056                            onResult(bundle);
2057                            return;
2058                        }
2059                        String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
2060                        if (authToken != null) {
2061                            String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2062                            String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
2063                            if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
2064                                onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
2065                                        "the type and name should not be empty");
2066                                return;
2067                            }
2068                            Account resultAccount = new Account(name, type);
2069                            if (!customTokens) {
2070                                saveAuthTokenToDatabase(
2071                                        mAccounts,
2072                                        resultAccount,
2073                                        authTokenType,
2074                                        authToken);
2075                            }
2076                            long expiryMillis = result.getLong(
2077                                    AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2078                            if (customTokens
2079                                    && expiryMillis > System.currentTimeMillis()) {
2080                                saveCachedToken(
2081                                        mAccounts,
2082                                        account,
2083                                        callerPkg,
2084                                        callerPkgSigDigest,
2085                                        authTokenType,
2086                                        authToken,
2087                                        expiryMillis);
2088                            }
2089                        }
2090
2091                        Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
2092                        if (intent != null && notifyOnAuthFailure && !customTokens) {
2093                            doNotification(mAccounts,
2094                                    account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
2095                                    intent, accounts.userId);
2096                        }
2097                    }
2098                    super.onResult(result);
2099                }
2100            }.bind();
2101        } finally {
2102            restoreCallingIdentity(identityToken);
2103        }
2104    }
2105
2106    private byte[] calculatePackageSignatureDigest(String callerPkg) {
2107        MessageDigest digester;
2108        try {
2109            digester = MessageDigest.getInstance("SHA-256");
2110            PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2111                    callerPkg, PackageManager.GET_SIGNATURES);
2112            for (Signature sig : pkgInfo.signatures) {
2113                digester.update(sig.toByteArray());
2114            }
2115        } catch (NoSuchAlgorithmException x) {
2116            Log.wtf(TAG, "SHA-256 should be available", x);
2117            digester = null;
2118        } catch (NameNotFoundException e) {
2119            Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2120            digester = null;
2121        }
2122        return (digester == null) ? null : digester.digest();
2123    }
2124
2125    private void createNoCredentialsPermissionNotification(Account account, Intent intent,
2126            int userId) {
2127        int uid = intent.getIntExtra(
2128                GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2129        String authTokenType = intent.getStringExtra(
2130                GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
2131        final String titleAndSubtitle =
2132                mContext.getString(R.string.permission_request_notification_with_subtitle,
2133                account.name);
2134        final int index = titleAndSubtitle.indexOf('\n');
2135        String title = titleAndSubtitle;
2136        String subtitle = "";
2137        if (index > 0) {
2138            title = titleAndSubtitle.substring(0, index);
2139            subtitle = titleAndSubtitle.substring(index + 1);
2140        }
2141        UserHandle user = new UserHandle(userId);
2142        Context contextForUser = getContextForUser(user);
2143        Notification n = new Notification.Builder(contextForUser)
2144                .setSmallIcon(android.R.drawable.stat_sys_warning)
2145                .setWhen(0)
2146                .setColor(contextForUser.getColor(
2147                        com.android.internal.R.color.system_notification_accent_color))
2148                .setContentTitle(title)
2149                .setContentText(subtitle)
2150                .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2151                        PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2152                .build();
2153        installNotification(getCredentialPermissionNotificationId(
2154                account, authTokenType, uid), n, user);
2155    }
2156
2157    private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
2158            AccountAuthenticatorResponse response, String authTokenType) {
2159
2160        Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
2161        // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
2162        // Since it was set in Eclair+ we can't change it without breaking apps using
2163        // the intent from a non-Activity context.
2164        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2165        intent.addCategory(
2166                String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
2167
2168        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
2169        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2170        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
2171        intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
2172
2173        return intent;
2174    }
2175
2176    private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2177            int uid) {
2178        Integer id;
2179        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
2180        synchronized (accounts.credentialsPermissionNotificationIds) {
2181            final Pair<Pair<Account, String>, Integer> key =
2182                    new Pair<Pair<Account, String>, Integer>(
2183                            new Pair<Account, String>(account, authTokenType), uid);
2184            id = accounts.credentialsPermissionNotificationIds.get(key);
2185            if (id == null) {
2186                id = mNotificationIds.incrementAndGet();
2187                accounts.credentialsPermissionNotificationIds.put(key, id);
2188            }
2189        }
2190        return id;
2191    }
2192
2193    private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
2194        Integer id;
2195        synchronized (accounts.signinRequiredNotificationIds) {
2196            id = accounts.signinRequiredNotificationIds.get(account);
2197            if (id == null) {
2198                id = mNotificationIds.incrementAndGet();
2199                accounts.signinRequiredNotificationIds.put(account, id);
2200            }
2201        }
2202        return id;
2203    }
2204
2205    @Override
2206    public void addAccount(final IAccountManagerResponse response, final String accountType,
2207            final String authTokenType, final String[] requiredFeatures,
2208            final boolean expectActivityLaunch, final Bundle optionsIn) {
2209        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2210            Log.v(TAG, "addAccount: accountType " + accountType
2211                    + ", response " + response
2212                    + ", authTokenType " + authTokenType
2213                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2214                    + ", expectActivityLaunch " + expectActivityLaunch
2215                    + ", caller's uid " + Binder.getCallingUid()
2216                    + ", pid " + Binder.getCallingPid());
2217        }
2218        if (response == null) throw new IllegalArgumentException("response is null");
2219        if (accountType == null) throw new IllegalArgumentException("accountType is null");
2220
2221        // Is user disallowed from modifying accounts?
2222        final int uid = Binder.getCallingUid();
2223        final int userId = UserHandle.getUserId(uid);
2224        if (!canUserModifyAccounts(userId, uid)) {
2225            try {
2226                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2227                        "User is not allowed to add an account!");
2228            } catch (RemoteException re) {
2229            }
2230            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2231            return;
2232        }
2233        if (!canUserModifyAccountsForType(userId, accountType, uid)) {
2234            try {
2235                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2236                        "User cannot modify accounts of this type (policy).");
2237            } catch (RemoteException re) {
2238            }
2239            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2240                    userId);
2241            return;
2242        }
2243
2244        final int pid = Binder.getCallingPid();
2245        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2246        options.putInt(AccountManager.KEY_CALLER_UID, uid);
2247        options.putInt(AccountManager.KEY_CALLER_PID, pid);
2248
2249        int usrId = UserHandle.getCallingUserId();
2250        long identityToken = clearCallingIdentity();
2251        try {
2252            UserAccounts accounts = getUserAccounts(usrId);
2253            logRecordWithUid(
2254                    accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
2255            new Session(accounts, response, accountType, expectActivityLaunch,
2256                    true /* stripAuthTokenFromResult */, null /* accountName */,
2257                    false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
2258                @Override
2259                public void run() throws RemoteException {
2260                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2261                            options);
2262                }
2263
2264                @Override
2265                protected String toDebugString(long now) {
2266                    return super.toDebugString(now) + ", addAccount"
2267                            + ", accountType " + accountType
2268                            + ", requiredFeatures "
2269                            + (requiredFeatures != null
2270                              ? TextUtils.join(",", requiredFeatures)
2271                              : null);
2272                }
2273            }.bind();
2274        } finally {
2275            restoreCallingIdentity(identityToken);
2276        }
2277    }
2278
2279    @Override
2280    public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
2281            final String authTokenType, final String[] requiredFeatures,
2282            final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
2283        int callingUid = Binder.getCallingUid();
2284        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2285            Log.v(TAG, "addAccount: accountType " + accountType
2286                    + ", response " + response
2287                    + ", authTokenType " + authTokenType
2288                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2289                    + ", expectActivityLaunch " + expectActivityLaunch
2290                    + ", caller's uid " + Binder.getCallingUid()
2291                    + ", pid " + Binder.getCallingPid()
2292                    + ", for user id " + userId);
2293        }
2294        if (response == null) throw new IllegalArgumentException("response is null");
2295        if (accountType == null) throw new IllegalArgumentException("accountType is null");
2296        // Only allow the system process to add accounts of other users
2297        if (isCrossUser(callingUid, userId)) {
2298            throw new SecurityException(
2299                    String.format(
2300                            "User %s trying to add account for %s" ,
2301                            UserHandle.getCallingUserId(),
2302                            userId));
2303        }
2304
2305        // Is user disallowed from modifying accounts?
2306        if (!canUserModifyAccounts(userId, callingUid)) {
2307            try {
2308                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2309                        "User is not allowed to add an account!");
2310            } catch (RemoteException re) {
2311            }
2312            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2313            return;
2314        }
2315        if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
2316            try {
2317                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2318                        "User cannot modify accounts of this type (policy).");
2319            } catch (RemoteException re) {
2320            }
2321            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2322                    userId);
2323            return;
2324        }
2325
2326        final int pid = Binder.getCallingPid();
2327        final int uid = Binder.getCallingUid();
2328        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2329        options.putInt(AccountManager.KEY_CALLER_UID, uid);
2330        options.putInt(AccountManager.KEY_CALLER_PID, pid);
2331
2332        long identityToken = clearCallingIdentity();
2333        try {
2334            UserAccounts accounts = getUserAccounts(userId);
2335            logRecordWithUid(
2336                    accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
2337            new Session(accounts, response, accountType, expectActivityLaunch,
2338                    true /* stripAuthTokenFromResult */, null /* accountName */,
2339                    false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
2340                @Override
2341                public void run() throws RemoteException {
2342                    mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2343                            options);
2344                }
2345
2346                @Override
2347                protected String toDebugString(long now) {
2348                    return super.toDebugString(now) + ", addAccount"
2349                            + ", accountType " + accountType
2350                            + ", requiredFeatures "
2351                            + (requiredFeatures != null
2352                              ? TextUtils.join(",", requiredFeatures)
2353                              : null);
2354                }
2355            }.bind();
2356        } finally {
2357            restoreCallingIdentity(identityToken);
2358        }
2359    }
2360
2361    @Override
2362    public void startAddAccountSession(
2363            final IAccountManagerResponse response,
2364            final String accountType,
2365            final String authTokenType,
2366            final String[] requiredFeatures,
2367            final boolean expectActivityLaunch,
2368            final Bundle optionsIn) {
2369        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2370            Log.v(TAG,
2371                    "startAddAccountSession: accountType " + accountType
2372                    + ", response " + response
2373                    + ", authTokenType " + authTokenType
2374                    + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2375                    + ", expectActivityLaunch " + expectActivityLaunch
2376                    + ", caller's uid " + Binder.getCallingUid()
2377                    + ", pid " + Binder.getCallingPid());
2378        }
2379        if (response == null) {
2380            throw new IllegalArgumentException("response is null");
2381        }
2382        if (accountType == null) {
2383            throw new IllegalArgumentException("accountType is null");
2384        }
2385
2386        final int uid = Binder.getCallingUid();
2387        // Only allow system to start session
2388        if (!isSystemUid(uid)) {
2389            String msg = String.format(
2390                    "uid %s cannot stat add account session.",
2391                    uid);
2392            throw new SecurityException(msg);
2393        }
2394
2395        final int userId = UserHandle.getUserId(uid);
2396        if (!canUserModifyAccounts(userId, uid)) {
2397            try {
2398                response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2399                        "User is not allowed to add an account!");
2400            } catch (RemoteException re) {
2401            }
2402            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2403            return;
2404        }
2405        if (!canUserModifyAccountsForType(userId, accountType, uid)) {
2406            try {
2407                response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2408                        "User cannot modify accounts of this type (policy).");
2409            } catch (RemoteException re) {
2410            }
2411            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2412                    userId);
2413            return;
2414        }
2415
2416        final int pid = Binder.getCallingPid();
2417        final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2418        options.putInt(AccountManager.KEY_CALLER_UID, uid);
2419        options.putInt(AccountManager.KEY_CALLER_PID, pid);
2420
2421        int usrId = UserHandle.getCallingUserId();
2422        long identityToken = clearCallingIdentity();
2423        try {
2424            UserAccounts accounts = getUserAccounts(usrId);
2425            logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
2426                    TABLE_ACCOUNTS, uid);
2427            new StartAccountSession(accounts, response, accountType, expectActivityLaunch,
2428                    null /* accountName */, false /* authDetailsRequired */,
2429                    true /* updateLastAuthenticationTime */) {
2430                @Override
2431                public void run() throws RemoteException {
2432                    mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
2433                            requiredFeatures, options);
2434                }
2435
2436                @Override
2437                protected String toDebugString(long now) {
2438                    String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
2439                    return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
2440                            + accountType + ", requiredFeatures "
2441                            + (requiredFeatures != null ? requiredFeaturesStr : null);
2442                }
2443            }.bind();
2444        } finally {
2445            restoreCallingIdentity(identityToken);
2446        }
2447    }
2448
2449    /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
2450    private abstract class StartAccountSession extends Session {
2451
2452        public StartAccountSession(UserAccounts accounts, IAccountManagerResponse response,
2453                String accountType, boolean expectActivityLaunch, String accountName,
2454                boolean authDetailsRequired, boolean updateLastAuthenticationTime) {
2455            super(accounts, response, accountType, expectActivityLaunch,
2456                    true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
2457                    updateLastAuthenticationTime);
2458        }
2459
2460        @Override
2461        public void onResult(Bundle result) {
2462            mNumResults++;
2463            Intent intent = null;
2464            if (result != null
2465                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
2466                checkKeyIntent(
2467                        Binder.getCallingUid(),
2468                        intent);
2469            }
2470            IAccountManagerResponse response;
2471            if (mExpectActivityLaunch && result != null
2472                    && result.containsKey(AccountManager.KEY_INTENT)) {
2473                response = mResponse;
2474            } else {
2475                response = getResponseAndClose();
2476            }
2477            if (response == null) {
2478                return;
2479            }
2480            if (result == null) {
2481                if (Log.isLoggable(TAG, Log.VERBOSE)) {
2482                    Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
2483                            + response);
2484                }
2485                sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2486                        "null bundle returned");
2487                return;
2488            }
2489
2490            if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
2491                // All AccountManager error codes are greater
2492                // than 0
2493                sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
2494                        result.getString(AccountManager.KEY_ERROR_MESSAGE));
2495                return;
2496            }
2497
2498            // Strip auth token from result.
2499            result.remove(AccountManager.KEY_AUTHTOKEN);
2500
2501            if (Log.isLoggable(TAG, Log.VERBOSE)) {
2502                Log.v(TAG,
2503                        getClass().getSimpleName() + " calling onResult() on response " + response);
2504            }
2505
2506            // Get the session bundle created by authenticator. The
2507            // bundle contains data necessary for finishing the session
2508            // later. The session bundle will be encrypted here and
2509            // decrypted later when trying to finish the session.
2510            Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
2511            if (sessionBundle != null) {
2512                String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2513                if (TextUtils.isEmpty(accountType)
2514                        || !mAccountType.equalsIgnoreCase(accountType)) {
2515                    Log.w(TAG, "Account type in session bundle doesn't match request.");
2516                }
2517                // Add accountType info to session bundle. This will
2518                // override any value set by authenticator.
2519                sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
2520
2521                // Encrypt session bundle before returning to caller.
2522                try {
2523                    CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2524                    Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
2525                    result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
2526                } catch (GeneralSecurityException e) {
2527                    if (Log.isLoggable(TAG, Log.DEBUG)) {
2528                        Log.v(TAG, "Failed to encrypt session bundle!", e);
2529                    }
2530                    sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2531                            "failed to encrypt session bundle");
2532                    return;
2533                }
2534            }
2535
2536            sendResponse(response, result);
2537        }
2538    }
2539
2540    @Override
2541    public void finishSessionAsUser(IAccountManagerResponse response,
2542            @NonNull Bundle sessionBundle,
2543            boolean expectActivityLaunch,
2544            Bundle appInfo,
2545            int userId) {
2546        int callingUid = Binder.getCallingUid();
2547        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2548            Log.v(TAG,
2549                    "finishSession: response "+ response
2550                            + ", expectActivityLaunch " + expectActivityLaunch
2551                            + ", caller's uid " + callingUid
2552                            + ", caller's user id " + UserHandle.getCallingUserId()
2553                            + ", pid " + Binder.getCallingPid()
2554                            + ", for user id " + userId);
2555        }
2556        if (response == null) {
2557            throw new IllegalArgumentException("response is null");
2558        }
2559
2560        // Session bundle is the encrypted bundle of the original bundle created by authenticator.
2561        // Account type is added to it before encryption.
2562        if (sessionBundle == null || sessionBundle.size() == 0) {
2563            throw new IllegalArgumentException("sessionBundle is empty");
2564        }
2565
2566        // Only allow the system process to finish session for other users
2567        if (isCrossUser(callingUid, userId)) {
2568            throw new SecurityException(
2569                    String.format(
2570                            "User %s trying to finish session for %s without cross user permission",
2571                            UserHandle.getCallingUserId(),
2572                            userId));
2573        }
2574
2575        // Only allow system to finish session
2576        if (!isSystemUid(callingUid)) {
2577            String msg = String.format(
2578                    "uid %s cannot finish session because it's not system uid.",
2579                    callingUid);
2580            throw new SecurityException(msg);
2581        }
2582
2583        if (!canUserModifyAccounts(userId, callingUid)) {
2584            sendErrorResponse(response,
2585                    AccountManager.ERROR_CODE_USER_RESTRICTED,
2586                    "User is not allowed to add an account!");
2587            showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2588            return;
2589        }
2590
2591        final int pid = Binder.getCallingPid();
2592        final Bundle decryptedBundle;
2593        final String accountType;
2594        // First decrypt session bundle to get account type for checking permission.
2595        try {
2596            CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2597            decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
2598            if (decryptedBundle == null) {
2599                sendErrorResponse(
2600                        response,
2601                        AccountManager.ERROR_CODE_BAD_REQUEST,
2602                        "failed to decrypt session bundle");
2603                return;
2604            }
2605            accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2606            // Account type cannot be null. This should not happen if session bundle was created
2607            // properly by #StartAccountSession.
2608            if (TextUtils.isEmpty(accountType)) {
2609                sendErrorResponse(
2610                        response,
2611                        AccountManager.ERROR_CODE_BAD_ARGUMENTS,
2612                        "accountType is empty");
2613                return;
2614            }
2615
2616            // If by any chances, decryptedBundle contains colliding keys with
2617            // system info
2618            // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
2619            // update credentials flow, we should replace with the new values of the current call.
2620            if (appInfo != null) {
2621                decryptedBundle.putAll(appInfo);
2622            }
2623
2624            // Add info that may be used by add account or update credentials flow.
2625            decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
2626            decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
2627        } catch (GeneralSecurityException e) {
2628            if (Log.isLoggable(TAG, Log.DEBUG)) {
2629                Log.v(TAG, "Failed to decrypt session bundle!", e);
2630            }
2631            sendErrorResponse(
2632                    response,
2633                    AccountManager.ERROR_CODE_BAD_REQUEST,
2634                    "failed to decrypt session bundle");
2635            return;
2636        }
2637
2638        if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
2639            sendErrorResponse(
2640                    response,
2641                    AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2642                    "User cannot modify accounts of this type (policy).");
2643            showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2644                    userId);
2645            return;
2646        }
2647
2648        long identityToken = clearCallingIdentity();
2649        try {
2650            UserAccounts accounts = getUserAccounts(userId);
2651            logRecordWithUid(
2652                    accounts,
2653                    DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
2654                    TABLE_ACCOUNTS,
2655                    callingUid);
2656            new Session(
2657                    accounts,
2658                    response,
2659                    accountType,
2660                    expectActivityLaunch,
2661                    true /* stripAuthTokenFromResult */,
2662                    null /* accountName */,
2663                    false /* authDetailsRequired */,
2664                    true /* updateLastAuthenticationTime */) {
2665                @Override
2666                public void run() throws RemoteException {
2667                    mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
2668                }
2669
2670                @Override
2671                protected String toDebugString(long now) {
2672                    return super.toDebugString(now)
2673                            + ", finishSession"
2674                            + ", accountType " + accountType;
2675                }
2676            }.bind();
2677        } finally {
2678            restoreCallingIdentity(identityToken);
2679        }
2680    }
2681
2682    private void showCantAddAccount(int errorCode, int userId) {
2683        Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
2684        cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
2685        cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2686        long identityToken = clearCallingIdentity();
2687        try {
2688            mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId));
2689        } finally {
2690            restoreCallingIdentity(identityToken);
2691        }
2692    }
2693
2694    @Override
2695    public void confirmCredentialsAsUser(
2696            IAccountManagerResponse response,
2697            final Account account,
2698            final Bundle options,
2699            final boolean expectActivityLaunch,
2700            int userId) {
2701        int callingUid = Binder.getCallingUid();
2702        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2703            Log.v(TAG, "confirmCredentials: " + account
2704                    + ", response " + response
2705                    + ", expectActivityLaunch " + expectActivityLaunch
2706                    + ", caller's uid " + callingUid
2707                    + ", pid " + Binder.getCallingPid());
2708        }
2709        // Only allow the system process to read accounts of other users
2710        if (isCrossUser(callingUid, userId)) {
2711            throw new SecurityException(
2712                    String.format(
2713                            "User %s trying to confirm account credentials for %s" ,
2714                            UserHandle.getCallingUserId(),
2715                            userId));
2716        }
2717        if (response == null) throw new IllegalArgumentException("response is null");
2718        if (account == null) throw new IllegalArgumentException("account is null");
2719        long identityToken = clearCallingIdentity();
2720        try {
2721            UserAccounts accounts = getUserAccounts(userId);
2722            new Session(accounts, response, account.type, expectActivityLaunch,
2723                    true /* stripAuthTokenFromResult */, account.name,
2724                    true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
2725                @Override
2726                public void run() throws RemoteException {
2727                    mAuthenticator.confirmCredentials(this, account, options);
2728                }
2729                @Override
2730                protected String toDebugString(long now) {
2731                    return super.toDebugString(now) + ", confirmCredentials"
2732                            + ", " + account;
2733                }
2734            }.bind();
2735        } finally {
2736            restoreCallingIdentity(identityToken);
2737        }
2738    }
2739
2740    @Override
2741    public void updateCredentials(IAccountManagerResponse response, final Account account,
2742            final String authTokenType, final boolean expectActivityLaunch,
2743            final Bundle loginOptions) {
2744        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2745            Log.v(TAG, "updateCredentials: " + account
2746                    + ", response " + response
2747                    + ", authTokenType " + authTokenType
2748                    + ", expectActivityLaunch " + expectActivityLaunch
2749                    + ", caller's uid " + Binder.getCallingUid()
2750                    + ", pid " + Binder.getCallingPid());
2751        }
2752        if (response == null) throw new IllegalArgumentException("response is null");
2753        if (account == null) throw new IllegalArgumentException("account is null");
2754        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2755        int userId = UserHandle.getCallingUserId();
2756        long identityToken = clearCallingIdentity();
2757        try {
2758            UserAccounts accounts = getUserAccounts(userId);
2759            new Session(accounts, response, account.type, expectActivityLaunch,
2760                    true /* stripAuthTokenFromResult */, account.name,
2761                    false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
2762                @Override
2763                public void run() throws RemoteException {
2764                    mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
2765                }
2766                @Override
2767                protected String toDebugString(long now) {
2768                    if (loginOptions != null) loginOptions.keySet();
2769                    return super.toDebugString(now) + ", updateCredentials"
2770                            + ", " + account
2771                            + ", authTokenType " + authTokenType
2772                            + ", loginOptions " + loginOptions;
2773                }
2774            }.bind();
2775        } finally {
2776            restoreCallingIdentity(identityToken);
2777        }
2778    }
2779
2780    @Override
2781    public void startUpdateCredentialsSession(
2782            IAccountManagerResponse response,
2783            final Account account,
2784            final String authTokenType,
2785            final boolean expectActivityLaunch,
2786            final Bundle loginOptions) {
2787        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2788            Log.v(TAG,
2789                    "startUpdateCredentialsSession: " + account + ", response " + response
2790                            + ", authTokenType " + authTokenType + ", expectActivityLaunch "
2791                            + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
2792                            + ", pid " + Binder.getCallingPid());
2793        }
2794        if (response == null) {
2795            throw new IllegalArgumentException("response is null");
2796        }
2797        if (account == null) {
2798            throw new IllegalArgumentException("account is null");
2799        }
2800
2801        final int uid = Binder.getCallingUid();
2802        // Only allow system to start session
2803        if (!isSystemUid(uid)) {
2804            String msg = String.format(
2805                    "uid %s cannot start update credentials session.",
2806                    uid);
2807            throw new SecurityException(msg);
2808        }
2809
2810        int userId = UserHandle.getCallingUserId();
2811        long identityToken = clearCallingIdentity();
2812        try {
2813            UserAccounts accounts = getUserAccounts(userId);
2814            new StartAccountSession(
2815                    accounts,
2816                    response,
2817                    account.type,
2818                    expectActivityLaunch,
2819                    account.name,
2820                    false /* authDetailsRequired */,
2821                    true /* updateLastCredentialTime */) {
2822                @Override
2823                public void run() throws RemoteException {
2824                    mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
2825                            loginOptions);
2826                }
2827
2828                @Override
2829                protected String toDebugString(long now) {
2830                    if (loginOptions != null)
2831                        loginOptions.keySet();
2832                    return super.toDebugString(now)
2833                            + ", startUpdateCredentialsSession"
2834                            + ", " + account
2835                            + ", authTokenType " + authTokenType
2836                            + ", loginOptions " + loginOptions;
2837                }
2838            }.bind();
2839        } finally {
2840            restoreCallingIdentity(identityToken);
2841        }
2842    }
2843
2844    @Override
2845    public void isCredentialsUpdateSuggested(
2846            IAccountManagerResponse response,
2847            final Account account,
2848            final String statusToken) {
2849        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2850            Log.v(TAG,
2851                    "isCredentialsUpdateSuggested: " + account + ", response " + response
2852                            + ", caller's uid " + Binder.getCallingUid()
2853                            + ", pid " + Binder.getCallingPid());
2854        }
2855        if (response == null) {
2856            throw new IllegalArgumentException("response is null");
2857        }
2858        if (account == null) {
2859            throw new IllegalArgumentException("account is null");
2860        }
2861        if (TextUtils.isEmpty(statusToken)) {
2862            throw new IllegalArgumentException("status token is empty");
2863        }
2864
2865        int uid = Binder.getCallingUid();
2866        // Only allow system to start session
2867        if (!isSystemUid(uid)) {
2868            String msg = String.format(
2869                    "uid %s cannot stat add account session.",
2870                    uid);
2871            throw new SecurityException(msg);
2872        }
2873
2874        int usrId = UserHandle.getCallingUserId();
2875        long identityToken = clearCallingIdentity();
2876        try {
2877            UserAccounts accounts = getUserAccounts(usrId);
2878            new Session(accounts, response, account.type, false /* expectActivityLaunch */,
2879                    false /* stripAuthTokenFromResult */, account.name,
2880                    false /* authDetailsRequired */) {
2881                @Override
2882                protected String toDebugString(long now) {
2883                    return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
2884                            + ", " + account;
2885                }
2886
2887                @Override
2888                public void run() throws RemoteException {
2889                    mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
2890                }
2891
2892                @Override
2893                public void onResult(Bundle result) {
2894                    IAccountManagerResponse response = getResponseAndClose();
2895                    if (response == null) {
2896                        return;
2897                    }
2898
2899                    if (result == null) {
2900                        sendErrorResponse(
2901                                response,
2902                                AccountManager.ERROR_CODE_INVALID_RESPONSE,
2903                                "null bundle");
2904                        return;
2905                    }
2906
2907                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
2908                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2909                                + response);
2910                    }
2911                    // Check to see if an error occurred. We know if an error occurred because all
2912                    // error codes are greater than 0.
2913                    if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
2914                        sendErrorResponse(response,
2915                                result.getInt(AccountManager.KEY_ERROR_CODE),
2916                                result.getString(AccountManager.KEY_ERROR_MESSAGE));
2917                        return;
2918                    }
2919                    if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
2920                        sendErrorResponse(
2921                                response,
2922                                AccountManager.ERROR_CODE_INVALID_RESPONSE,
2923                                "no result in response");
2924                        return;
2925                    }
2926                    final Bundle newResult = new Bundle();
2927                    newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
2928                            result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
2929                    sendResponse(response, newResult);
2930                }
2931            }.bind();
2932        } finally {
2933            restoreCallingIdentity(identityToken);
2934        }
2935    }
2936
2937    @Override
2938    public void editProperties(IAccountManagerResponse response, final String accountType,
2939            final boolean expectActivityLaunch) {
2940        final int callingUid = Binder.getCallingUid();
2941        if (Log.isLoggable(TAG, Log.VERBOSE)) {
2942            Log.v(TAG, "editProperties: accountType " + accountType
2943                    + ", response " + response
2944                    + ", expectActivityLaunch " + expectActivityLaunch
2945                    + ", caller's uid " + callingUid
2946                    + ", pid " + Binder.getCallingPid());
2947        }
2948        if (response == null) throw new IllegalArgumentException("response is null");
2949        if (accountType == null) throw new IllegalArgumentException("accountType is null");
2950        int userId = UserHandle.getCallingUserId();
2951        if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
2952            String msg = String.format(
2953                    "uid %s cannot edit authenticator properites for account type: %s",
2954                    callingUid,
2955                    accountType);
2956            throw new SecurityException(msg);
2957        }
2958        long identityToken = clearCallingIdentity();
2959        try {
2960            UserAccounts accounts = getUserAccounts(userId);
2961            new Session(accounts, response, accountType, expectActivityLaunch,
2962                    true /* stripAuthTokenFromResult */, null /* accountName */,
2963                    false /* authDetailsRequired */) {
2964                @Override
2965                public void run() throws RemoteException {
2966                    mAuthenticator.editProperties(this, mAccountType);
2967                }
2968                @Override
2969                protected String toDebugString(long now) {
2970                    return super.toDebugString(now) + ", editProperties"
2971                            + ", accountType " + accountType;
2972                }
2973            }.bind();
2974        } finally {
2975            restoreCallingIdentity(identityToken);
2976        }
2977    }
2978
2979    @Override
2980    public boolean someUserHasAccount(@NonNull final Account account) {
2981        if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
2982            throw new SecurityException("Only system can check for accounts across users");
2983        }
2984        final long token = Binder.clearCallingIdentity();
2985        try {
2986            AccountAndUser[] allAccounts = getAllAccounts();
2987            for (int i = allAccounts.length - 1; i >= 0; i--) {
2988                if (allAccounts[i].account.equals(account)) {
2989                    return true;
2990                }
2991            }
2992            return false;
2993        } finally {
2994            Binder.restoreCallingIdentity(token);
2995        }
2996    }
2997
2998    private class GetAccountsByTypeAndFeatureSession extends Session {
2999        private final String[] mFeatures;
3000        private volatile Account[] mAccountsOfType = null;
3001        private volatile ArrayList<Account> mAccountsWithFeatures = null;
3002        private volatile int mCurrentAccount = 0;
3003        private final int mCallingUid;
3004
3005        public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
3006                IAccountManagerResponse response, String type, String[] features, int callingUid) {
3007            super(accounts, response, type, false /* expectActivityLaunch */,
3008                    true /* stripAuthTokenFromResult */, null /* accountName */,
3009                    false /* authDetailsRequired */);
3010            mCallingUid = callingUid;
3011            mFeatures = features;
3012        }
3013
3014        @Override
3015        public void run() throws RemoteException {
3016            synchronized (mAccounts.cacheLock) {
3017                mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
3018                        null);
3019            }
3020            // check whether each account matches the requested features
3021            mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
3022            mCurrentAccount = 0;
3023
3024            checkAccount();
3025        }
3026
3027        public void checkAccount() {
3028            if (mCurrentAccount >= mAccountsOfType.length) {
3029                sendResult();
3030                return;
3031            }
3032
3033            final IAccountAuthenticator accountAuthenticator = mAuthenticator;
3034            if (accountAuthenticator == null) {
3035                // It is possible that the authenticator has died, which is indicated by
3036                // mAuthenticator being set to null. If this happens then just abort.
3037                // There is no need to send back a result or error in this case since
3038                // that already happened when mAuthenticator was cleared.
3039                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3040                    Log.v(TAG, "checkAccount: aborting session since we are no longer"
3041                            + " connected to the authenticator, " + toDebugString());
3042                }
3043                return;
3044            }
3045            try {
3046                accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
3047            } catch (RemoteException e) {
3048                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
3049            }
3050        }
3051
3052        @Override
3053        public void onResult(Bundle result) {
3054            mNumResults++;
3055            if (result == null) {
3056                onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
3057                return;
3058            }
3059            if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
3060                mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3061            }
3062            mCurrentAccount++;
3063            checkAccount();
3064        }
3065
3066        public void sendResult() {
3067            IAccountManagerResponse response = getResponseAndClose();
3068            if (response != null) {
3069                try {
3070                    Account[] accounts = new Account[mAccountsWithFeatures.size()];
3071                    for (int i = 0; i < accounts.length; i++) {
3072                        accounts[i] = mAccountsWithFeatures.get(i);
3073                    }
3074                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3075                        Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3076                                + response);
3077                    }
3078                    Bundle result = new Bundle();
3079                    result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3080                    response.onResult(result);
3081                } catch (RemoteException e) {
3082                    // if the caller is dead then there is no one to care about remote exceptions
3083                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3084                        Log.v(TAG, "failure while notifying response", e);
3085                    }
3086                }
3087            }
3088        }
3089
3090
3091        @Override
3092        protected String toDebugString(long now) {
3093            return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3094                    + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3095        }
3096    }
3097
3098    /**
3099     * Returns the accounts visible to the client within the context of a specific user
3100     * @hide
3101     */
3102    @NonNull
3103    public Account[] getAccounts(int userId, String opPackageName) {
3104        int callingUid = Binder.getCallingUid();
3105        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3106                opPackageName);
3107        if (visibleAccountTypes.isEmpty()) {
3108            return new Account[0];
3109        }
3110        long identityToken = clearCallingIdentity();
3111        try {
3112            UserAccounts accounts = getUserAccounts(userId);
3113            return getAccountsInternal(
3114                    accounts,
3115                    callingUid,
3116                    null,  // packageName
3117                    visibleAccountTypes);
3118        } finally {
3119            restoreCallingIdentity(identityToken);
3120        }
3121    }
3122
3123    /**
3124     * Returns accounts for all running users.
3125     *
3126     * @hide
3127     */
3128    @NonNull
3129    public AccountAndUser[] getRunningAccounts() {
3130        final int[] runningUserIds;
3131        try {
3132            runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
3133        } catch (RemoteException e) {
3134            // Running in system_server; should never happen
3135            throw new RuntimeException(e);
3136        }
3137        return getAccounts(runningUserIds);
3138    }
3139
3140    /** {@hide} */
3141    @NonNull
3142    public AccountAndUser[] getAllAccounts() {
3143        final List<UserInfo> users = getUserManager().getUsers();
3144        final int[] userIds = new int[users.size()];
3145        for (int i = 0; i < userIds.length; i++) {
3146            userIds[i] = users.get(i).id;
3147        }
3148        return getAccounts(userIds);
3149    }
3150
3151    @NonNull
3152    private AccountAndUser[] getAccounts(int[] userIds) {
3153        final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
3154        for (int userId : userIds) {
3155            UserAccounts userAccounts = getUserAccounts(userId);
3156            if (userAccounts == null) continue;
3157            synchronized (userAccounts.cacheLock) {
3158                Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
3159                        Binder.getCallingUid(), null);
3160                for (int a = 0; a < accounts.length; a++) {
3161                    runningAccounts.add(new AccountAndUser(accounts[a], userId));
3162                }
3163            }
3164        }
3165
3166        AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
3167        return runningAccounts.toArray(accountsArray);
3168    }
3169
3170    @Override
3171    @NonNull
3172    public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
3173        return getAccountsAsUser(type, userId, null, -1, opPackageName);
3174    }
3175
3176    @NonNull
3177    private Account[] getAccountsAsUser(
3178            String type,
3179            int userId,
3180            String callingPackage,
3181            int packageUid,
3182            String opPackageName) {
3183        int callingUid = Binder.getCallingUid();
3184        // Only allow the system process to read accounts of other users
3185        if (userId != UserHandle.getCallingUserId()
3186                && callingUid != Process.myUid()
3187                && mContext.checkCallingOrSelfPermission(
3188                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
3189                    != PackageManager.PERMISSION_GRANTED) {
3190            throw new SecurityException("User " + UserHandle.getCallingUserId()
3191                    + " trying to get account for " + userId);
3192        }
3193
3194        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3195            Log.v(TAG, "getAccounts: accountType " + type
3196                    + ", caller's uid " + Binder.getCallingUid()
3197                    + ", pid " + Binder.getCallingPid());
3198        }
3199        // If the original calling app was using the framework account chooser activity, we'll
3200        // be passed in the original caller's uid here, which is what should be used for filtering.
3201        if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
3202            callingUid = packageUid;
3203            opPackageName = callingPackage;
3204        }
3205
3206        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3207                opPackageName);
3208        if (visibleAccountTypes.isEmpty()
3209                || (type != null && !visibleAccountTypes.contains(type))) {
3210            return new Account[0];
3211        } else if (visibleAccountTypes.contains(type)) {
3212            // Prune the list down to just the requested type.
3213            visibleAccountTypes = new ArrayList<>();
3214            visibleAccountTypes.add(type);
3215        } // else aggregate all the visible accounts (it won't matter if the
3216          // list is empty).
3217
3218        long identityToken = clearCallingIdentity();
3219        try {
3220            UserAccounts accounts = getUserAccounts(userId);
3221            return getAccountsInternal(
3222                    accounts,
3223                    callingUid,
3224                    callingPackage,
3225                    visibleAccountTypes);
3226        } finally {
3227            restoreCallingIdentity(identityToken);
3228        }
3229    }
3230
3231    @NonNull
3232    private Account[] getAccountsInternal(
3233            UserAccounts userAccounts,
3234            int callingUid,
3235            String callingPackage,
3236            List<String> visibleAccountTypes) {
3237        synchronized (userAccounts.cacheLock) {
3238            ArrayList<Account> visibleAccounts = new ArrayList<>();
3239            for (String visibleType : visibleAccountTypes) {
3240                Account[] accountsForType = getAccountsFromCacheLocked(
3241                        userAccounts, visibleType, callingUid, callingPackage);
3242                if (accountsForType != null) {
3243                    visibleAccounts.addAll(Arrays.asList(accountsForType));
3244                }
3245            }
3246            Account[] result = new Account[visibleAccounts.size()];
3247            for (int i = 0; i < visibleAccounts.size(); i++) {
3248                result[i] = visibleAccounts.get(i);
3249            }
3250            return result;
3251        }
3252    }
3253
3254    @Override
3255    public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
3256        checkManageUsersPermission("addSharedAccountsFromParentUser");
3257        Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
3258        for (Account account : accounts) {
3259            addSharedAccountAsUser(account, userId);
3260        }
3261    }
3262
3263    private boolean addSharedAccountAsUser(Account account, int userId) {
3264        userId = handleIncomingUser(userId);
3265        UserAccounts accounts = getUserAccounts(userId);
3266        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3267        ContentValues values = new ContentValues();
3268        values.put(ACCOUNTS_NAME, account.name);
3269        values.put(ACCOUNTS_TYPE, account.type);
3270        db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3271                new String[] {account.name, account.type});
3272        long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
3273        if (accountId < 0) {
3274            Log.w(TAG, "insertAccountIntoDatabase: " + account
3275                    + ", skipping the DB insert failed");
3276            return false;
3277        }
3278        logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
3279        return true;
3280    }
3281
3282    @Override
3283    public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
3284        userId = handleIncomingUser(userId);
3285        UserAccounts accounts = getUserAccounts(userId);
3286        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3287        long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
3288        final ContentValues values = new ContentValues();
3289        values.put(ACCOUNTS_NAME, newName);
3290        values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
3291        int r = db.update(
3292                TABLE_SHARED_ACCOUNTS,
3293                values,
3294                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3295                new String[] { account.name, account.type });
3296        if (r > 0) {
3297            int callingUid = getCallingUid();
3298            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
3299                    sharedTableAccountId, accounts, callingUid);
3300            // Recursively rename the account.
3301            renameAccountInternal(accounts, account, newName);
3302        }
3303        return r > 0;
3304    }
3305
3306    @Override
3307    public boolean removeSharedAccountAsUser(Account account, int userId) {
3308        return removeSharedAccountAsUser(account, userId, getCallingUid());
3309    }
3310
3311    private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
3312        userId = handleIncomingUser(userId);
3313        UserAccounts accounts = getUserAccounts(userId);
3314        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3315        long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
3316        int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3317                new String[] {account.name, account.type});
3318        if (r > 0) {
3319            logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
3320                    sharedTableAccountId, accounts, callingUid);
3321            removeAccountInternal(accounts, account, callingUid);
3322        }
3323        return r > 0;
3324    }
3325
3326    @Override
3327    public Account[] getSharedAccountsAsUser(int userId) {
3328        userId = handleIncomingUser(userId);
3329        UserAccounts accounts = getUserAccounts(userId);
3330        ArrayList<Account> accountList = new ArrayList<Account>();
3331        Cursor cursor = null;
3332        try {
3333            cursor = accounts.openHelper.getReadableDatabase()
3334                    .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
3335                    null, null, null, null, null);
3336            if (cursor != null && cursor.moveToFirst()) {
3337                int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
3338                int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
3339                do {
3340                    accountList.add(new Account(cursor.getString(nameIndex),
3341                            cursor.getString(typeIndex)));
3342                } while (cursor.moveToNext());
3343            }
3344        } finally {
3345            if (cursor != null) {
3346                cursor.close();
3347            }
3348        }
3349        Account[] accountArray = new Account[accountList.size()];
3350        accountList.toArray(accountArray);
3351        return accountArray;
3352    }
3353
3354    @Override
3355    @NonNull
3356    public Account[] getAccounts(String type, String opPackageName) {
3357        return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
3358    }
3359
3360    @Override
3361    @NonNull
3362    public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
3363        int callingUid = Binder.getCallingUid();
3364        if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
3365            throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
3366                    + callingUid + " with uid=" + uid);
3367        }
3368        return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
3369                opPackageName);
3370    }
3371
3372    @Override
3373    @NonNull
3374    public Account[] getAccountsByTypeForPackage(String type, String packageName,
3375            String opPackageName) {
3376        int packageUid = -1;
3377        try {
3378            packageUid = AppGlobals.getPackageManager().getPackageUid(
3379                    packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
3380                    UserHandle.getCallingUserId());
3381        } catch (RemoteException re) {
3382            Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
3383            return new Account[0];
3384        }
3385        return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
3386                packageUid, opPackageName);
3387    }
3388
3389    @Override
3390    public void getAccountsByFeatures(
3391            IAccountManagerResponse response,
3392            String type,
3393            String[] features,
3394            String opPackageName) {
3395        int callingUid = Binder.getCallingUid();
3396        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3397            Log.v(TAG, "getAccounts: accountType " + type
3398                    + ", response " + response
3399                    + ", features " + stringArrayToString(features)
3400                    + ", caller's uid " + callingUid
3401                    + ", pid " + Binder.getCallingPid());
3402        }
3403        if (response == null) throw new IllegalArgumentException("response is null");
3404        if (type == null) throw new IllegalArgumentException("accountType is null");
3405        int userId = UserHandle.getCallingUserId();
3406
3407        List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3408                opPackageName);
3409        if (!visibleAccountTypes.contains(type)) {
3410            Bundle result = new Bundle();
3411            // Need to return just the accounts that are from matching signatures.
3412            result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
3413            try {
3414                response.onResult(result);
3415            } catch (RemoteException e) {
3416                Log.e(TAG, "Cannot respond to caller do to exception." , e);
3417            }
3418            return;
3419        }
3420        long identityToken = clearCallingIdentity();
3421        try {
3422            UserAccounts userAccounts = getUserAccounts(userId);
3423            if (features == null || features.length == 0) {
3424                Account[] accounts;
3425                synchronized (userAccounts.cacheLock) {
3426                    accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
3427                }
3428                Bundle result = new Bundle();
3429                result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3430                onResult(response, result);
3431                return;
3432            }
3433            new GetAccountsByTypeAndFeatureSession(
3434                    userAccounts,
3435                    response,
3436                    type,
3437                    features,
3438                    callingUid).bind();
3439        } finally {
3440            restoreCallingIdentity(identityToken);
3441        }
3442    }
3443
3444    private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) {
3445        Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
3446                "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
3447        try {
3448            if (cursor.moveToNext()) {
3449                return cursor.getLong(0);
3450            }
3451            return -1;
3452        } finally {
3453            cursor.close();
3454        }
3455    }
3456
3457    private long getAccountIdLocked(SQLiteDatabase db, Account account) {
3458        Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
3459                "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
3460        try {
3461            if (cursor.moveToNext()) {
3462                return cursor.getLong(0);
3463            }
3464            return -1;
3465        } finally {
3466            cursor.close();
3467        }
3468    }
3469
3470    private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
3471        Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
3472                EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
3473                new String[]{key}, null, null, null);
3474        try {
3475            if (cursor.moveToNext()) {
3476                return cursor.getLong(0);
3477            }
3478            return -1;
3479        } finally {
3480            cursor.close();
3481        }
3482    }
3483
3484    private abstract class Session extends IAccountAuthenticatorResponse.Stub
3485            implements IBinder.DeathRecipient, ServiceConnection {
3486        IAccountManagerResponse mResponse;
3487        final String mAccountType;
3488        final boolean mExpectActivityLaunch;
3489        final long mCreationTime;
3490        final String mAccountName;
3491        // Indicates if we need to add auth details(like last credential time)
3492        final boolean mAuthDetailsRequired;
3493        // If set, we need to update the last authenticated time. This is
3494        // currently
3495        // used on
3496        // successful confirming credentials.
3497        final boolean mUpdateLastAuthenticatedTime;
3498
3499        public int mNumResults = 0;
3500        private int mNumRequestContinued = 0;
3501        private int mNumErrors = 0;
3502
3503        IAccountAuthenticator mAuthenticator = null;
3504
3505        private final boolean mStripAuthTokenFromResult;
3506        protected final UserAccounts mAccounts;
3507
3508        public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
3509                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3510                boolean authDetailsRequired) {
3511            this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
3512                    accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
3513        }
3514
3515        public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
3516                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3517                boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
3518            super();
3519            //if (response == null) throw new IllegalArgumentException("response is null");
3520            if (accountType == null) throw new IllegalArgumentException("accountType is null");
3521            mAccounts = accounts;
3522            mStripAuthTokenFromResult = stripAuthTokenFromResult;
3523            mResponse = response;
3524            mAccountType = accountType;
3525            mExpectActivityLaunch = expectActivityLaunch;
3526            mCreationTime = SystemClock.elapsedRealtime();
3527            mAccountName = accountName;
3528            mAuthDetailsRequired = authDetailsRequired;
3529            mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
3530
3531            synchronized (mSessions) {
3532                mSessions.put(toString(), this);
3533            }
3534            if (response != null) {
3535                try {
3536                    response.asBinder().linkToDeath(this, 0 /* flags */);
3537                } catch (RemoteException e) {
3538                    mResponse = null;
3539                    binderDied();
3540                }
3541            }
3542        }
3543
3544        IAccountManagerResponse getResponseAndClose() {
3545            if (mResponse == null) {
3546                // this session has already been closed
3547                return null;
3548            }
3549            IAccountManagerResponse response = mResponse;
3550            close(); // this clears mResponse so we need to save the response before this call
3551            return response;
3552        }
3553
3554        /**
3555         * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
3556         * security policy.
3557         *
3558         * In particular we want to make sure that the Authenticator doesn't try to trick users
3559         * into launching aribtrary intents on the device via by tricking to click authenticator
3560         * supplied entries in the system Settings app.
3561         */
3562        protected void checkKeyIntent(
3563                int authUid,
3564                Intent intent) throws SecurityException {
3565            long bid = Binder.clearCallingIdentity();
3566            try {
3567                PackageManager pm = mContext.getPackageManager();
3568                ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
3569                ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
3570                int targetUid = targetActivityInfo.applicationInfo.uid;
3571                if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid, targetUid)) {
3572                    String pkgName = targetActivityInfo.packageName;
3573                    String activityName = targetActivityInfo.name;
3574                    String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
3575                            + "does not share a signature with the supplying authenticator (%s).";
3576                    throw new SecurityException(
3577                            String.format(tmpl, activityName, pkgName, mAccountType));
3578                }
3579            } finally {
3580                Binder.restoreCallingIdentity(bid);
3581            }
3582        }
3583
3584        private void close() {
3585            synchronized (mSessions) {
3586                if (mSessions.remove(toString()) == null) {
3587                    // the session was already closed, so bail out now
3588                    return;
3589                }
3590            }
3591            if (mResponse != null) {
3592                // stop listening for response deaths
3593                mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
3594
3595                // clear this so that we don't accidentally send any further results
3596                mResponse = null;
3597            }
3598            cancelTimeout();
3599            unbind();
3600        }
3601
3602        @Override
3603        public void binderDied() {
3604            mResponse = null;
3605            close();
3606        }
3607
3608        protected String toDebugString() {
3609            return toDebugString(SystemClock.elapsedRealtime());
3610        }
3611
3612        protected String toDebugString(long now) {
3613            return "Session: expectLaunch " + mExpectActivityLaunch
3614                    + ", connected " + (mAuthenticator != null)
3615                    + ", stats (" + mNumResults + "/" + mNumRequestContinued
3616                    + "/" + mNumErrors + ")"
3617                    + ", lifetime " + ((now - mCreationTime) / 1000.0);
3618        }
3619
3620        void bind() {
3621            if (Log.isLoggable(TAG, Log.VERBOSE)) {
3622                Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
3623            }
3624            if (!bindToAuthenticator(mAccountType)) {
3625                Log.d(TAG, "bind attempt failed for " + toDebugString());
3626                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
3627            }
3628        }
3629
3630        private void unbind() {
3631            if (mAuthenticator != null) {
3632                mAuthenticator = null;
3633                mContext.unbindService(this);
3634            }
3635        }
3636
3637        public void cancelTimeout() {
3638            mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
3639        }
3640
3641        @Override
3642        public void onServiceConnected(ComponentName name, IBinder service) {
3643            mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
3644            try {
3645                run();
3646            } catch (RemoteException e) {
3647                onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3648                        "remote exception");
3649            }
3650        }
3651
3652        @Override
3653        public void onServiceDisconnected(ComponentName name) {
3654            mAuthenticator = null;
3655            IAccountManagerResponse response = getResponseAndClose();
3656            if (response != null) {
3657                try {
3658                    response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3659                            "disconnected");
3660                } catch (RemoteException e) {
3661                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3662                        Log.v(TAG, "Session.onServiceDisconnected: "
3663                                + "caught RemoteException while responding", e);
3664                    }
3665                }
3666            }
3667        }
3668
3669        public abstract void run() throws RemoteException;
3670
3671        public void onTimedOut() {
3672            IAccountManagerResponse response = getResponseAndClose();
3673            if (response != null) {
3674                try {
3675                    response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3676                            "timeout");
3677                } catch (RemoteException e) {
3678                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3679                        Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
3680                                e);
3681                    }
3682                }
3683            }
3684        }
3685
3686        @Override
3687        public void onResult(Bundle result) {
3688            mNumResults++;
3689            Intent intent = null;
3690            if (result != null) {
3691                boolean isSuccessfulConfirmCreds = result.getBoolean(
3692                        AccountManager.KEY_BOOLEAN_RESULT, false);
3693                boolean isSuccessfulUpdateCredsOrAddAccount =
3694                        result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
3695                        && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
3696                // We should only update lastAuthenticated time, if
3697                // mUpdateLastAuthenticatedTime is true and the confirmRequest
3698                // or updateRequest was successful
3699                boolean needUpdate = mUpdateLastAuthenticatedTime
3700                        && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
3701                if (needUpdate || mAuthDetailsRequired) {
3702                    boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
3703                    if (needUpdate && accountPresent) {
3704                        updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
3705                    }
3706                    if (mAuthDetailsRequired) {
3707                        long lastAuthenticatedTime = -1;
3708                        if (accountPresent) {
3709                            lastAuthenticatedTime = DatabaseUtils.longForQuery(
3710                                    mAccounts.openHelper.getReadableDatabase(),
3711                                    "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
3712                                            + " from " +
3713                                            TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
3714                                            + ACCOUNTS_TYPE + "=?",
3715                                    new String[] {
3716                                            mAccountName, mAccountType
3717                                    });
3718                        }
3719                        result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
3720                                lastAuthenticatedTime);
3721                    }
3722                }
3723            }
3724            if (result != null
3725                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
3726                checkKeyIntent(
3727                        Binder.getCallingUid(),
3728                        intent);
3729            }
3730            if (result != null
3731                    && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
3732                String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
3733                String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
3734                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
3735                    Account account = new Account(accountName, accountType);
3736                    cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
3737                            new UserHandle(mAccounts.userId));
3738                }
3739            }
3740            IAccountManagerResponse response;
3741            if (mExpectActivityLaunch && result != null
3742                    && result.containsKey(AccountManager.KEY_INTENT)) {
3743                response = mResponse;
3744            } else {
3745                response = getResponseAndClose();
3746            }
3747            if (response != null) {
3748                try {
3749                    if (result == null) {
3750                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3751                            Log.v(TAG, getClass().getSimpleName()
3752                                    + " calling onError() on response " + response);
3753                        }
3754                        response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
3755                                "null bundle returned");
3756                    } else {
3757                        if (mStripAuthTokenFromResult) {
3758                            result.remove(AccountManager.KEY_AUTHTOKEN);
3759                        }
3760                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
3761                            Log.v(TAG, getClass().getSimpleName()
3762                                    + " calling onResult() on response " + response);
3763                        }
3764                        if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
3765                                (intent == null)) {
3766                            // All AccountManager error codes are greater than 0
3767                            response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
3768                                    result.getString(AccountManager.KEY_ERROR_MESSAGE));
3769                        } else {
3770                            response.onResult(result);
3771                        }
3772                    }
3773                } catch (RemoteException e) {
3774                    // if the caller is dead then there is no one to care about remote exceptions
3775                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3776                        Log.v(TAG, "failure while notifying response", e);
3777                    }
3778                }
3779            }
3780        }
3781
3782        @Override
3783        public void onRequestContinued() {
3784            mNumRequestContinued++;
3785        }
3786
3787        @Override
3788        public void onError(int errorCode, String errorMessage) {
3789            mNumErrors++;
3790            IAccountManagerResponse response = getResponseAndClose();
3791            if (response != null) {
3792                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3793                    Log.v(TAG, getClass().getSimpleName()
3794                            + " calling onError() on response " + response);
3795                }
3796                try {
3797                    response.onError(errorCode, errorMessage);
3798                } catch (RemoteException e) {
3799                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
3800                        Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
3801                    }
3802                }
3803            } else {
3804                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3805                    Log.v(TAG, "Session.onError: already closed");
3806                }
3807            }
3808        }
3809
3810        /**
3811         * find the component name for the authenticator and initiate a bind
3812         * if no authenticator or the bind fails then return false, otherwise return true
3813         */
3814        private boolean bindToAuthenticator(String authenticatorType) {
3815            final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
3816            authenticatorInfo = mAuthenticatorCache.getServiceInfo(
3817                    AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
3818            if (authenticatorInfo == null) {
3819                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3820                    Log.v(TAG, "there is no authenticator for " + authenticatorType
3821                            + ", bailing out");
3822                }
3823                return false;
3824            }
3825
3826            final ActivityManager am = mContext.getSystemService(ActivityManager.class);
3827            if (am.isUserRunningAndLocked(mAccounts.userId)
3828                    && !authenticatorInfo.componentInfo.directBootAware) {
3829                Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
3830                        + " which isn't encryption aware");
3831                return false;
3832            }
3833
3834            Intent intent = new Intent();
3835            intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
3836            intent.setComponent(authenticatorInfo.componentName);
3837            if (Log.isLoggable(TAG, Log.VERBOSE)) {
3838                Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
3839            }
3840            if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
3841                    new UserHandle(mAccounts.userId))) {
3842                if (Log.isLoggable(TAG, Log.VERBOSE)) {
3843                    Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
3844                }
3845                return false;
3846            }
3847
3848            return true;
3849        }
3850    }
3851
3852    private class MessageHandler extends Handler {
3853        MessageHandler(Looper looper) {
3854            super(looper);
3855        }
3856
3857        @Override
3858        public void handleMessage(Message msg) {
3859            switch (msg.what) {
3860                case MESSAGE_TIMED_OUT:
3861                    Session session = (Session)msg.obj;
3862                    session.onTimedOut();
3863                    break;
3864
3865                case MESSAGE_COPY_SHARED_ACCOUNT:
3866                    copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
3867                    break;
3868
3869                default:
3870                    throw new IllegalStateException("unhandled message: " + msg.what);
3871            }
3872        }
3873    }
3874
3875    private static String getDatabaseName(int userId) {
3876        File systemDir = Environment.getDataSystemDirectory();
3877        File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
3878        if (userId == 0) {
3879            // Migrate old file, if it exists, to the new location.
3880            // Make sure the new file doesn't already exist. A dummy file could have been
3881            // accidentally created in the old location, causing the new one to become corrupted
3882            // as well.
3883            File oldFile = new File(systemDir, DATABASE_NAME);
3884            if (oldFile.exists() && !databaseFile.exists()) {
3885                // Check for use directory; create if it doesn't exist, else renameTo will fail
3886                File userDir = Environment.getUserSystemDirectory(userId);
3887                if (!userDir.exists()) {
3888                    if (!userDir.mkdirs()) {
3889                        throw new IllegalStateException("User dir cannot be created: " + userDir);
3890                    }
3891                }
3892                if (!oldFile.renameTo(databaseFile)) {
3893                    throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
3894                }
3895            }
3896        }
3897        return databaseFile.getPath();
3898    }
3899
3900    private static class DebugDbHelper{
3901        private DebugDbHelper() {
3902        }
3903
3904        private static String TABLE_DEBUG = "debug_table";
3905
3906        // Columns for the table
3907        private static String ACTION_TYPE = "action_type";
3908        private static String TIMESTAMP = "time";
3909        private static String CALLER_UID = "caller_uid";
3910        private static String TABLE_NAME = "table_name";
3911        private static String KEY = "primary_key";
3912
3913        // These actions correspond to the occurrence of real actions. Since
3914        // these are called by the authenticators, the uid associated will be
3915        // of the authenticator.
3916        private static String ACTION_SET_PASSWORD = "action_set_password";
3917        private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
3918        private static String ACTION_ACCOUNT_ADD = "action_account_add";
3919        private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
3920        private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
3921        private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
3922
3923        // These actions don't necessarily correspond to any action on
3924        // accountDb taking place. As an example, there might be a request for
3925        // addingAccount, which might not lead to addition of account on grounds
3926        // of bad authentication. We will still be logging it to keep track of
3927        // who called.
3928        private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
3929        private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
3930
3931        //This action doesn't add account to accountdb. Account is only
3932        // added in finishSession which may be in a different user profile.
3933        private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
3934        private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
3935                "action_called_account_session_finish";
3936
3937        private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
3938
3939        private static void createDebugTable(SQLiteDatabase db) {
3940            db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
3941                    + ACCOUNTS_ID + " INTEGER,"
3942                    + ACTION_TYPE + " TEXT NOT NULL, "
3943                    + TIMESTAMP + " DATETIME,"
3944                    + CALLER_UID + " INTEGER NOT NULL,"
3945                    + TABLE_NAME + " TEXT NOT NULL,"
3946                    + KEY + " INTEGER PRIMARY KEY)");
3947            db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
3948        }
3949    }
3950
3951    private void logRecord(UserAccounts accounts, String action, String tableName) {
3952        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3953        logRecord(db, action, tableName, -1, accounts);
3954    }
3955
3956    private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
3957        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3958        logRecord(db, action, tableName, -1, accounts, uid);
3959    }
3960
3961    /*
3962     * This function receives an opened writable database.
3963     */
3964    private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3965            UserAccounts userAccount) {
3966        logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
3967    }
3968
3969    /*
3970     * This function receives an opened writable database.
3971     */
3972    private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3973            UserAccounts userAccount, int callingUid) {
3974        SQLiteStatement logStatement = userAccount.statementForLogging;
3975        logStatement.bindLong(1, accountId);
3976        logStatement.bindString(2, action);
3977        logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
3978        logStatement.bindLong(4, callingUid);
3979        logStatement.bindString(5, tableName);
3980        logStatement.bindLong(6, userAccount.debugDbInsertionPoint);
3981        logStatement.execute();
3982        logStatement.clearBindings();
3983        userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
3984                % MAX_DEBUG_DB_SIZE;
3985    }
3986
3987    /*
3988     * This should only be called once to compile the sql statement for logging
3989     * and to find the insertion point.
3990     */
3991    private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
3992            UserAccounts userAccount) {
3993        // Initialize the count if not done earlier.
3994        int size = (int) getDebugTableRowCount(db);
3995        if (size >= MAX_DEBUG_DB_SIZE) {
3996            // Table is full, and we need to find the point where to insert.
3997            userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
3998        } else {
3999            userAccount.debugDbInsertionPoint = size;
4000        }
4001        compileSqlStatementForLogging(db, userAccount);
4002    }
4003
4004    private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
4005        String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
4006                + " VALUES (?,?,?,?,?,?)";
4007        userAccount.statementForLogging = db.compileStatement(sql);
4008    }
4009
4010    private long getDebugTableRowCount(SQLiteDatabase db) {
4011        String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
4012        return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4013    }
4014
4015    /*
4016     * Finds the row key where the next insertion should take place. This should
4017     * be invoked only if the table has reached its full capacity.
4018     */
4019    private long getDebugTableInsertionPoint(SQLiteDatabase db) {
4020        // This query finds the smallest timestamp value (and if 2 records have
4021        // same timestamp, the choose the lower id).
4022        String queryCountDebugDbRows = new StringBuilder()
4023                .append("SELECT ").append(DebugDbHelper.KEY)
4024                .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
4025                .append(" ORDER BY ")
4026                .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
4027                .append(" LIMIT 1")
4028                .toString();
4029        return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
4030    }
4031
4032    static class DatabaseHelper extends SQLiteOpenHelper {
4033
4034        private final Context mContext;
4035        private final int mUserId;
4036
4037        public DatabaseHelper(Context context, int userId) {
4038            super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
4039            mContext = context;
4040            mUserId = userId;
4041        }
4042
4043        /**
4044         * This call needs to be made while the mCacheLock is held. The way to
4045         * ensure this is to get the lock any time a method is called ont the DatabaseHelper
4046         * @param db The database.
4047         */
4048        @Override
4049        public void onCreate(SQLiteDatabase db) {
4050            db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
4051                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4052                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4053                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4054                    + ACCOUNTS_PASSWORD + " TEXT, "
4055                    + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
4056                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
4057                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4058
4059            db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
4060                    + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,  "
4061                    + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4062                    + AUTHTOKENS_TYPE + " TEXT NOT NULL,  "
4063                    + AUTHTOKENS_AUTHTOKEN + " TEXT,  "
4064                    + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
4065
4066            createGrantsTable(db);
4067
4068            db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
4069                    + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4070                    + EXTRAS_ACCOUNTS_ID + " INTEGER, "
4071                    + EXTRAS_KEY + " TEXT NOT NULL, "
4072                    + EXTRAS_VALUE + " TEXT, "
4073                    + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
4074
4075            db.execSQL("CREATE TABLE " + TABLE_META + " ( "
4076                    + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
4077                    + META_VALUE + " TEXT)");
4078
4079            createSharedAccountsTable(db);
4080
4081            createAccountsDeletionTrigger(db);
4082
4083            DebugDbHelper.createDebugTable(db);
4084        }
4085
4086        private void createSharedAccountsTable(SQLiteDatabase db) {
4087            db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4088                    + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4089                    + ACCOUNTS_NAME + " TEXT NOT NULL, "
4090                    + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4091                    + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4092        }
4093
4094        private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
4095            db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
4096                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
4097        }
4098
4099        private void addOldAccountNameColumn(SQLiteDatabase db) {
4100            db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
4101        }
4102
4103        private void addDebugTable(SQLiteDatabase db) {
4104            DebugDbHelper.createDebugTable(db);
4105        }
4106
4107        private void createAccountsDeletionTrigger(SQLiteDatabase db) {
4108            db.execSQL(""
4109                    + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4110                    + " BEGIN"
4111                    + "   DELETE FROM " + TABLE_AUTHTOKENS
4112                    + "     WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4113                    + "   DELETE FROM " + TABLE_EXTRAS
4114                    + "     WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4115                    + "   DELETE FROM " + TABLE_GRANTS
4116                    + "     WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4117                    + " END");
4118        }
4119
4120        private void createGrantsTable(SQLiteDatabase db) {
4121            db.execSQL("CREATE TABLE " + TABLE_GRANTS + " (  "
4122                    + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4123                    + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL,  "
4124                    + GRANTS_GRANTEE_UID + " INTEGER NOT NULL,  "
4125                    + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4126                    +   "," + GRANTS_GRANTEE_UID + "))");
4127        }
4128
4129        private void populateMetaTableWithAuthTypeAndUID(
4130                SQLiteDatabase db,
4131                Map<String, Integer> authTypeAndUIDMap) {
4132            Iterator<Entry<String, Integer>> iterator = authTypeAndUIDMap.entrySet().iterator();
4133            while (iterator.hasNext()) {
4134                Entry<String, Integer> entry = iterator.next();
4135                ContentValues values = new ContentValues();
4136                values.put(META_KEY,
4137                        META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + entry.getKey());
4138                values.put(META_VALUE, entry.getValue());
4139                db.insert(TABLE_META, null, values);
4140            }
4141        }
4142
4143        @Override
4144        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
4145            Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
4146
4147            if (oldVersion == 1) {
4148                // no longer need to do anything since the work is done
4149                // when upgrading from version 2
4150                oldVersion++;
4151            }
4152
4153            if (oldVersion == 2) {
4154                createGrantsTable(db);
4155                db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
4156                createAccountsDeletionTrigger(db);
4157                oldVersion++;
4158            }
4159
4160            if (oldVersion == 3) {
4161                db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
4162                        " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
4163                oldVersion++;
4164            }
4165
4166            if (oldVersion == 4) {
4167                createSharedAccountsTable(db);
4168                oldVersion++;
4169            }
4170
4171            if (oldVersion == 5) {
4172                addOldAccountNameColumn(db);
4173                oldVersion++;
4174            }
4175
4176            if (oldVersion == 6) {
4177                addLastSuccessfullAuthenticatedTimeColumn(db);
4178                oldVersion++;
4179            }
4180
4181            if (oldVersion == 7) {
4182                addDebugTable(db);
4183                oldVersion++;
4184            }
4185
4186            if (oldVersion == 8) {
4187                populateMetaTableWithAuthTypeAndUID(
4188                        db,
4189                        AccountManagerService.getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
4190                oldVersion++;
4191            }
4192
4193            if (oldVersion != newVersion) {
4194                Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4195            }
4196        }
4197
4198        @Override
4199        public void onOpen(SQLiteDatabase db) {
4200            if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
4201        }
4202    }
4203
4204    public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
4205        return asBinder();
4206    }
4207
4208    /**
4209     * Searches array of arguments for the specified string
4210     * @param args array of argument strings
4211     * @param value value to search for
4212     * @return true if the value is contained in the array
4213     */
4214    private static boolean scanArgs(String[] args, String value) {
4215        if (args != null) {
4216            for (String arg : args) {
4217                if (value.equals(arg)) {
4218                    return true;
4219                }
4220            }
4221        }
4222        return false;
4223    }
4224
4225    @Override
4226    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
4227        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
4228                != PackageManager.PERMISSION_GRANTED) {
4229            fout.println("Permission Denial: can't dump AccountsManager from from pid="
4230                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
4231                    + " without permission " + android.Manifest.permission.DUMP);
4232            return;
4233        }
4234        final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
4235        final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, "  ");
4236
4237        final List<UserInfo> users = getUserManager().getUsers();
4238        for (UserInfo user : users) {
4239            ipw.println("User " + user + ":");
4240            ipw.increaseIndent();
4241            dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
4242            ipw.println();
4243            ipw.decreaseIndent();
4244        }
4245    }
4246
4247    private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
4248            String[] args, boolean isCheckinRequest) {
4249        synchronized (userAccounts.cacheLock) {
4250            final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
4251
4252            if (isCheckinRequest) {
4253                // This is a checkin request. *Only* upload the account types and the count of each.
4254                Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
4255                        null, null, ACCOUNTS_TYPE, null, null);
4256                try {
4257                    while (cursor.moveToNext()) {
4258                        // print type,count
4259                        fout.println(cursor.getString(0) + "," + cursor.getString(1));
4260                    }
4261                } finally {
4262                    if (cursor != null) {
4263                        cursor.close();
4264                    }
4265                }
4266            } else {
4267                Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
4268                        Process.myUid(), null);
4269                fout.println("Accounts: " + accounts.length);
4270                for (Account account : accounts) {
4271                    fout.println("  " + account);
4272                }
4273
4274                // Add debug information.
4275                fout.println();
4276                Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
4277                        null, null, null, null, DebugDbHelper.TIMESTAMP);
4278                fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
4279                fout.println("Accounts History");
4280                try {
4281                    while (cursor.moveToNext()) {
4282                        // print type,count
4283                        fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
4284                                cursor.getString(2) + "," + cursor.getString(3) + ","
4285                                + cursor.getString(4) + "," + cursor.getString(5));
4286                    }
4287                } finally {
4288                    cursor.close();
4289                }
4290
4291                fout.println();
4292                synchronized (mSessions) {
4293                    final long now = SystemClock.elapsedRealtime();
4294                    fout.println("Active Sessions: " + mSessions.size());
4295                    for (Session session : mSessions.values()) {
4296                        fout.println("  " + session.toDebugString(now));
4297                    }
4298                }
4299
4300                fout.println();
4301                mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
4302            }
4303        }
4304    }
4305
4306    private void doNotification(UserAccounts accounts, Account account, CharSequence message,
4307            Intent intent, int userId) {
4308        long identityToken = clearCallingIdentity();
4309        try {
4310            if (Log.isLoggable(TAG, Log.VERBOSE)) {
4311                Log.v(TAG, "doNotification: " + message + " intent:" + intent);
4312            }
4313
4314            if (intent.getComponent() != null &&
4315                    GrantCredentialsPermissionActivity.class.getName().equals(
4316                            intent.getComponent().getClassName())) {
4317                createNoCredentialsPermissionNotification(account, intent, userId);
4318            } else {
4319                final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
4320                intent.addCategory(String.valueOf(notificationId));
4321                UserHandle user = new UserHandle(userId);
4322                Context contextForUser = getContextForUser(user);
4323                final String notificationTitleFormat =
4324                        contextForUser.getText(R.string.notification_title).toString();
4325                Notification n = new Notification.Builder(contextForUser)
4326                        .setWhen(0)
4327                        .setSmallIcon(android.R.drawable.stat_sys_warning)
4328                        .setColor(contextForUser.getColor(
4329                                com.android.internal.R.color.system_notification_accent_color))
4330                        .setContentTitle(String.format(notificationTitleFormat, account.name))
4331                        .setContentText(message)
4332                        .setContentIntent(PendingIntent.getActivityAsUser(
4333                                mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
4334                                null, user))
4335                        .build();
4336                installNotification(notificationId, n, user);
4337            }
4338        } finally {
4339            restoreCallingIdentity(identityToken);
4340        }
4341    }
4342
4343    protected void installNotification(final int notificationId, final Notification n,
4344            UserHandle user) {
4345        ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
4346                .notifyAsUser(null, notificationId, n, user);
4347    }
4348
4349    protected void cancelNotification(int id, UserHandle user) {
4350        long identityToken = clearCallingIdentity();
4351        try {
4352            ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
4353                .cancelAsUser(null, id, user);
4354        } finally {
4355            restoreCallingIdentity(identityToken);
4356        }
4357    }
4358
4359    private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
4360        for (String perm : permissions) {
4361            if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
4362                if (Log.isLoggable(TAG, Log.VERBOSE)) {
4363                    Log.v(TAG, "  caller uid " + callingUid + " has " + perm);
4364                }
4365                final int opCode = AppOpsManager.permissionToOpCode(perm);
4366                if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
4367                        opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
4368                    return true;
4369                }
4370            }
4371        }
4372        return false;
4373    }
4374
4375    private int handleIncomingUser(int userId) {
4376        try {
4377            return ActivityManagerNative.getDefault().handleIncomingUser(
4378                    Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
4379        } catch (RemoteException re) {
4380            // Shouldn't happen, local.
4381        }
4382        return userId;
4383    }
4384
4385    private boolean isPrivileged(int callingUid) {
4386        final int callingUserId = UserHandle.getUserId(callingUid);
4387
4388        final PackageManager userPackageManager;
4389        try {
4390            userPackageManager = mContext.createPackageContextAsUser(
4391                    "android", 0, new UserHandle(callingUserId)).getPackageManager();
4392        } catch (NameNotFoundException e) {
4393            return false;
4394        }
4395
4396        String[] packages = userPackageManager.getPackagesForUid(callingUid);
4397        for (String name : packages) {
4398            try {
4399                PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
4400                if (packageInfo != null
4401                        && (packageInfo.applicationInfo.privateFlags
4402                                & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
4403                    return true;
4404                }
4405            } catch (PackageManager.NameNotFoundException e) {
4406                return false;
4407            }
4408        }
4409        return false;
4410    }
4411
4412    private boolean permissionIsGranted(
4413            Account account, String authTokenType, int callerUid, int userId) {
4414        final boolean isPrivileged = isPrivileged(callerUid);
4415        final boolean fromAuthenticator = account != null
4416                && isAccountManagedByCaller(account.type, callerUid, userId);
4417        final boolean hasExplicitGrants = account != null
4418                && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
4419        if (Log.isLoggable(TAG, Log.VERBOSE)) {
4420            Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
4421                    + callerUid + ", " + account
4422                    + ": is authenticator? " + fromAuthenticator
4423                    + ", has explicit permission? " + hasExplicitGrants);
4424        }
4425        return fromAuthenticator || hasExplicitGrants || isPrivileged;
4426    }
4427
4428    private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
4429            String opPackageName) {
4430        if (accountType == null) {
4431            return false;
4432        } else {
4433            return getTypesVisibleToCaller(callingUid, userId,
4434                    opPackageName).contains(accountType);
4435        }
4436    }
4437
4438    private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
4439        if (accountType == null) {
4440            return false;
4441        } else {
4442            return getTypesManagedByCaller(callingUid, userId).contains(accountType);
4443        }
4444    }
4445
4446    private List<String> getTypesVisibleToCaller(int callingUid, int userId,
4447            String opPackageName) {
4448        boolean isPermitted =
4449                isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
4450                        Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
4451        return getTypesForCaller(callingUid, userId, isPermitted);
4452    }
4453
4454    private List<String> getTypesManagedByCaller(int callingUid, int userId) {
4455        return getTypesForCaller(callingUid, userId, false);
4456    }
4457
4458    private List<String> getTypesForCaller(
4459            int callingUid, int userId, boolean isOtherwisePermitted) {
4460        List<String> managedAccountTypes = new ArrayList<>();
4461        long identityToken = Binder.clearCallingIdentity();
4462        Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
4463        try {
4464            serviceInfos = mAuthenticatorCache.getAllServices(userId);
4465        } finally {
4466            Binder.restoreCallingIdentity(identityToken);
4467        }
4468        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
4469                serviceInfos) {
4470            final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
4471            if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
4472                managedAccountTypes.add(serviceInfo.type.type);
4473            }
4474        }
4475        return managedAccountTypes;
4476    }
4477
4478    private boolean isAccountPresentForCaller(String accountName, String accountType) {
4479        if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
4480            for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
4481                if (account.name.equals(accountName)) {
4482                    return true;
4483                }
4484            }
4485        }
4486        return false;
4487    }
4488
4489    private static void checkManageUsersPermission(String message) {
4490        if (ActivityManager.checkComponentPermission(
4491                android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
4492                != PackageManager.PERMISSION_GRANTED) {
4493            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
4494        }
4495    }
4496
4497    private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
4498            int callerUid) {
4499        if (callerUid == Process.SYSTEM_UID) {
4500            return true;
4501        }
4502        UserAccounts accounts = getUserAccountsForCaller();
4503        synchronized (accounts.cacheLock) {
4504            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4505            String[] args = { String.valueOf(callerUid), authTokenType,
4506                    account.name, account.type};
4507            final boolean permissionGranted =
4508                    DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
4509            if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
4510                // TODO: Skip this check when running automated tests. Replace this
4511                // with a more general solution.
4512                Log.d(TAG, "no credentials permission for usage of " + account + ", "
4513                        + authTokenType + " by uid " + callerUid
4514                        + " but ignoring since device is in test harness.");
4515                return true;
4516            }
4517            return permissionGranted;
4518        }
4519    }
4520
4521    private boolean isSystemUid(int callingUid) {
4522        String[] packages = null;
4523        long ident = Binder.clearCallingIdentity();
4524        try {
4525            packages = mPackageManager.getPackagesForUid(callingUid);
4526        } finally {
4527            Binder.restoreCallingIdentity(ident);
4528        }
4529        if (packages != null) {
4530            for (String name : packages) {
4531                try {
4532                    PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
4533                    if (packageInfo != null
4534                            && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
4535                                    != 0) {
4536                        return true;
4537                    }
4538                } catch (PackageManager.NameNotFoundException e) {
4539                    Log.w(TAG, String.format("Could not find package [%s]", name), e);
4540                }
4541            }
4542        } else {
4543            Log.w(TAG, "No known packages with uid " + callingUid);
4544        }
4545        return false;
4546    }
4547
4548    /** Succeeds if any of the specified permissions are granted. */
4549    private void checkReadAccountsPermitted(
4550            int callingUid,
4551            String accountType,
4552            int userId,
4553            String opPackageName) {
4554        if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
4555            String msg = String.format(
4556                    "caller uid %s cannot access %s accounts",
4557                    callingUid,
4558                    accountType);
4559            Log.w(TAG, "  " + msg);
4560            throw new SecurityException(msg);
4561        }
4562    }
4563
4564    private boolean canUserModifyAccounts(int userId, int callingUid) {
4565        // the managing app can always modify accounts
4566        if (isProfileOwner(callingUid)) {
4567            return true;
4568        }
4569        if (getUserManager().getUserRestrictions(new UserHandle(userId))
4570                .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
4571            return false;
4572        }
4573        return true;
4574    }
4575
4576    private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
4577        // the managing app can always modify accounts
4578        if (isProfileOwner(callingUid)) {
4579            return true;
4580        }
4581        DevicePolicyManager dpm = (DevicePolicyManager) mContext
4582                .getSystemService(Context.DEVICE_POLICY_SERVICE);
4583        String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
4584        if (typesArray == null) {
4585            return true;
4586        }
4587        for (String forbiddenType : typesArray) {
4588            if (forbiddenType.equals(accountType)) {
4589                return false;
4590            }
4591        }
4592        return true;
4593    }
4594
4595    private boolean isProfileOwner(int uid) {
4596        final DevicePolicyManagerInternal dpmi =
4597                LocalServices.getService(DevicePolicyManagerInternal.class);
4598        return (dpmi != null)
4599                && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
4600    }
4601
4602    @Override
4603    public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
4604            throws RemoteException {
4605        final int callingUid = getCallingUid();
4606
4607        if (callingUid != Process.SYSTEM_UID) {
4608            throw new SecurityException();
4609        }
4610
4611        if (value) {
4612            grantAppPermission(account, authTokenType, uid);
4613        } else {
4614            revokeAppPermission(account, authTokenType, uid);
4615        }
4616    }
4617
4618    /**
4619     * Allow callers with the given uid permission to get credentials for account/authTokenType.
4620     * <p>
4621     * Although this is public it can only be accessed via the AccountManagerService object
4622     * which is in the system. This means we don't need to protect it with permissions.
4623     * @hide
4624     */
4625    private void grantAppPermission(Account account, String authTokenType, int uid) {
4626        if (account == null || authTokenType == null) {
4627            Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
4628            return;
4629        }
4630        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
4631        synchronized (accounts.cacheLock) {
4632            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4633            db.beginTransaction();
4634            try {
4635                long accountId = getAccountIdLocked(db, account);
4636                if (accountId >= 0) {
4637                    ContentValues values = new ContentValues();
4638                    values.put(GRANTS_ACCOUNTS_ID, accountId);
4639                    values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
4640                    values.put(GRANTS_GRANTEE_UID, uid);
4641                    db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
4642                    db.setTransactionSuccessful();
4643                }
4644            } finally {
4645                db.endTransaction();
4646            }
4647            cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
4648                    new UserHandle(accounts.userId));
4649        }
4650    }
4651
4652    /**
4653     * Don't allow callers with the given uid permission to get credentials for
4654     * account/authTokenType.
4655     * <p>
4656     * Although this is public it can only be accessed via the AccountManagerService object
4657     * which is in the system. This means we don't need to protect it with permissions.
4658     * @hide
4659     */
4660    private void revokeAppPermission(Account account, String authTokenType, int uid) {
4661        if (account == null || authTokenType == null) {
4662            Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
4663            return;
4664        }
4665        UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
4666        synchronized (accounts.cacheLock) {
4667            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4668            db.beginTransaction();
4669            try {
4670                long accountId = getAccountIdLocked(db, account);
4671                if (accountId >= 0) {
4672                    db.delete(TABLE_GRANTS,
4673                            GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
4674                                    + GRANTS_GRANTEE_UID + "=?",
4675                            new String[]{String.valueOf(accountId), authTokenType,
4676                                    String.valueOf(uid)});
4677                    db.setTransactionSuccessful();
4678                }
4679            } finally {
4680                db.endTransaction();
4681            }
4682            cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
4683                    new UserHandle(accounts.userId));
4684        }
4685    }
4686
4687    static final private String stringArrayToString(String[] value) {
4688        return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
4689    }
4690
4691    private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
4692        final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
4693        if (oldAccountsForType != null) {
4694            ArrayList<Account> newAccountsList = new ArrayList<Account>();
4695            for (Account curAccount : oldAccountsForType) {
4696                if (!curAccount.equals(account)) {
4697                    newAccountsList.add(curAccount);
4698                }
4699            }
4700            if (newAccountsList.isEmpty()) {
4701                accounts.accountCache.remove(account.type);
4702            } else {
4703                Account[] newAccountsForType = new Account[newAccountsList.size()];
4704                newAccountsForType = newAccountsList.toArray(newAccountsForType);
4705                accounts.accountCache.put(account.type, newAccountsForType);
4706            }
4707        }
4708        accounts.userDataCache.remove(account);
4709        accounts.authTokenCache.remove(account);
4710        accounts.previousNameCache.remove(account);
4711    }
4712
4713    /**
4714     * This assumes that the caller has already checked that the account is not already present.
4715     */
4716    private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
4717        Account[] accountsForType = accounts.accountCache.get(account.type);
4718        int oldLength = (accountsForType != null) ? accountsForType.length : 0;
4719        Account[] newAccountsForType = new Account[oldLength + 1];
4720        if (accountsForType != null) {
4721            System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
4722        }
4723        newAccountsForType[oldLength] = account;
4724        accounts.accountCache.put(account.type, newAccountsForType);
4725    }
4726
4727    private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
4728            int callingUid, String callingPackage) {
4729        if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
4730                || callingUid == Process.myUid()) {
4731            return unfiltered;
4732        }
4733        UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
4734        if (user != null && user.isRestricted()) {
4735            String[] packages = mPackageManager.getPackagesForUid(callingUid);
4736            // If any of the packages is a white listed package, return the full set,
4737            // otherwise return non-shared accounts only.
4738            // This might be a temporary way to specify a whitelist
4739            String whiteList = mContext.getResources().getString(
4740                    com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
4741            for (String packageName : packages) {
4742                if (whiteList.contains(";" + packageName + ";")) {
4743                    return unfiltered;
4744                }
4745            }
4746            ArrayList<Account> allowed = new ArrayList<Account>();
4747            Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
4748            if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
4749            String requiredAccountType = "";
4750            try {
4751                // If there's an explicit callingPackage specified, check if that package
4752                // opted in to see restricted accounts.
4753                if (callingPackage != null) {
4754                    PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
4755                    if (pi != null && pi.restrictedAccountType != null) {
4756                        requiredAccountType = pi.restrictedAccountType;
4757                    }
4758                } else {
4759                    // Otherwise check if the callingUid has a package that has opted in
4760                    for (String packageName : packages) {
4761                        PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
4762                        if (pi != null && pi.restrictedAccountType != null) {
4763                            requiredAccountType = pi.restrictedAccountType;
4764                            break;
4765                        }
4766                    }
4767                }
4768            } catch (NameNotFoundException nnfe) {
4769            }
4770            for (Account account : unfiltered) {
4771                if (account.type.equals(requiredAccountType)) {
4772                    allowed.add(account);
4773                } else {
4774                    boolean found = false;
4775                    for (Account shared : sharedAccounts) {
4776                        if (shared.equals(account)) {
4777                            found = true;
4778                            break;
4779                        }
4780                    }
4781                    if (!found) {
4782                        allowed.add(account);
4783                    }
4784                }
4785            }
4786            Account[] filtered = new Account[allowed.size()];
4787            allowed.toArray(filtered);
4788            return filtered;
4789        } else {
4790            return unfiltered;
4791        }
4792    }
4793
4794    /*
4795     * packageName can be null. If not null, it should be used to filter out restricted accounts
4796     * that the package is not allowed to access.
4797     */
4798    protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
4799            int callingUid, String callingPackage) {
4800        if (accountType != null) {
4801            final Account[] accounts = userAccounts.accountCache.get(accountType);
4802            if (accounts == null) {
4803                return EMPTY_ACCOUNT_ARRAY;
4804            } else {
4805                return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
4806                        callingUid, callingPackage);
4807            }
4808        } else {
4809            int totalLength = 0;
4810            for (Account[] accounts : userAccounts.accountCache.values()) {
4811                totalLength += accounts.length;
4812            }
4813            if (totalLength == 0) {
4814                return EMPTY_ACCOUNT_ARRAY;
4815            }
4816            Account[] accounts = new Account[totalLength];
4817            totalLength = 0;
4818            for (Account[] accountsOfType : userAccounts.accountCache.values()) {
4819                System.arraycopy(accountsOfType, 0, accounts, totalLength,
4820                        accountsOfType.length);
4821                totalLength += accountsOfType.length;
4822            }
4823            return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
4824        }
4825    }
4826
4827    protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4828            Account account, String key, String value) {
4829        HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
4830        if (userDataForAccount == null) {
4831            userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
4832            accounts.userDataCache.put(account, userDataForAccount);
4833        }
4834        if (value == null) {
4835            userDataForAccount.remove(key);
4836        } else {
4837            userDataForAccount.put(key, value);
4838        }
4839    }
4840
4841    protected String readCachedTokenInternal(
4842            UserAccounts accounts,
4843            Account account,
4844            String tokenType,
4845            String callingPackage,
4846            byte[] pkgSigDigest) {
4847        synchronized (accounts.cacheLock) {
4848            return accounts.accountTokenCaches.get(
4849                    account, tokenType, callingPackage, pkgSigDigest);
4850        }
4851    }
4852
4853    protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4854            Account account, String key, String value) {
4855        HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
4856        if (authTokensForAccount == null) {
4857            authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
4858            accounts.authTokenCache.put(account, authTokensForAccount);
4859        }
4860        if (value == null) {
4861            authTokensForAccount.remove(key);
4862        } else {
4863            authTokensForAccount.put(key, value);
4864        }
4865    }
4866
4867    protected String readAuthTokenInternal(UserAccounts accounts, Account account,
4868            String authTokenType) {
4869        synchronized (accounts.cacheLock) {
4870            HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
4871            if (authTokensForAccount == null) {
4872                // need to populate the cache for this account
4873                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4874                authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
4875                accounts.authTokenCache.put(account, authTokensForAccount);
4876            }
4877            return authTokensForAccount.get(authTokenType);
4878        }
4879    }
4880
4881    protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
4882        synchronized (accounts.cacheLock) {
4883            HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
4884            if (userDataForAccount == null) {
4885                // need to populate the cache for this account
4886                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4887                userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
4888                accounts.userDataCache.put(account, userDataForAccount);
4889            }
4890            return userDataForAccount.get(key);
4891        }
4892    }
4893
4894    protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
4895            final SQLiteDatabase db, Account account) {
4896        HashMap<String, String> userDataForAccount = new HashMap<String, String>();
4897        Cursor cursor = db.query(TABLE_EXTRAS,
4898                COLUMNS_EXTRAS_KEY_AND_VALUE,
4899                SELECTION_USERDATA_BY_ACCOUNT,
4900                new String[]{account.name, account.type},
4901                null, null, null);
4902        try {
4903            while (cursor.moveToNext()) {
4904                final String tmpkey = cursor.getString(0);
4905                final String value = cursor.getString(1);
4906                userDataForAccount.put(tmpkey, value);
4907            }
4908        } finally {
4909            cursor.close();
4910        }
4911        return userDataForAccount;
4912    }
4913
4914    protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
4915            final SQLiteDatabase db, Account account) {
4916        HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
4917        Cursor cursor = db.query(TABLE_AUTHTOKENS,
4918                COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
4919                SELECTION_AUTHTOKENS_BY_ACCOUNT,
4920                new String[]{account.name, account.type},
4921                null, null, null);
4922        try {
4923            while (cursor.moveToNext()) {
4924                final String type = cursor.getString(0);
4925                final String authToken = cursor.getString(1);
4926                authTokensForAccount.put(type, authToken);
4927            }
4928        } finally {
4929            cursor.close();
4930        }
4931        return authTokensForAccount;
4932    }
4933
4934    private Context getContextForUser(UserHandle user) {
4935        try {
4936            return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
4937        } catch (NameNotFoundException e) {
4938            // Default to mContext, not finding the package system is running as is unlikely.
4939            return mContext;
4940        }
4941    }
4942
4943    private void sendResponse(IAccountManagerResponse response, Bundle result) {
4944        try {
4945            response.onResult(result);
4946        } catch (RemoteException e) {
4947            // if the caller is dead then there is no one to care about remote
4948            // exceptions
4949            if (Log.isLoggable(TAG, Log.VERBOSE)) {
4950                Log.v(TAG, "failure while notifying response", e);
4951            }
4952        }
4953    }
4954
4955    private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
4956            String errorMessage) {
4957        try {
4958            response.onError(errorCode, errorMessage);
4959        } catch (RemoteException e) {
4960            // if the caller is dead then there is no one to care about remote
4961            // exceptions
4962            if (Log.isLoggable(TAG, Log.VERBOSE)) {
4963                Log.v(TAG, "failure while notifying response", e);
4964            }
4965        }
4966    }
4967}
4968