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