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