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