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