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