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