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 
17 package com.android.server.accounts;
18 
19 import android.Manifest;
20 import android.accounts.AbstractAccountAuthenticator;
21 import android.accounts.Account;
22 import android.accounts.AccountAndUser;
23 import android.accounts.AccountAuthenticatorResponse;
24 import android.accounts.AccountManager;
25 import android.accounts.AccountManagerInternal;
26 import android.accounts.AccountManagerResponse;
27 import android.accounts.AuthenticatorDescription;
28 import android.accounts.CantAddAccountActivity;
29 import android.accounts.ChooseAccountActivity;
30 import android.accounts.GrantCredentialsPermissionActivity;
31 import android.accounts.IAccountAuthenticator;
32 import android.accounts.IAccountAuthenticatorResponse;
33 import android.accounts.IAccountManager;
34 import android.accounts.IAccountManagerResponse;
35 import android.annotation.IntRange;
36 import android.annotation.NonNull;
37 import android.annotation.Nullable;
38 import android.app.ActivityManager;
39 import android.app.ActivityThread;
40 import android.app.AppOpsManager;
41 import android.app.INotificationManager;
42 import android.app.Notification;
43 import android.app.NotificationManager;
44 import android.app.PendingIntent;
45 import android.app.admin.DeviceAdminInfo;
46 import android.app.admin.DevicePolicyManager;
47 import android.app.admin.DevicePolicyManagerInternal;
48 import android.content.BroadcastReceiver;
49 import android.content.ComponentName;
50 import android.content.Context;
51 import android.content.Intent;
52 import android.content.IntentFilter;
53 import android.content.IntentSender;
54 import android.content.ServiceConnection;
55 import android.content.pm.ActivityInfo;
56 import android.content.pm.ApplicationInfo;
57 import android.content.pm.IPackageManager;
58 import android.content.pm.PackageInfo;
59 import android.content.pm.PackageManager;
60 import android.content.pm.PackageManager.NameNotFoundException;
61 import android.content.pm.PackageManagerInternal;
62 import android.content.pm.PackageParser;
63 import android.content.pm.RegisteredServicesCache;
64 import android.content.pm.RegisteredServicesCacheListener;
65 import android.content.pm.ResolveInfo;
66 import android.content.pm.Signature;
67 import android.content.pm.UserInfo;
68 import android.database.Cursor;
69 import android.database.sqlite.SQLiteStatement;
70 import android.os.Binder;
71 import android.os.Bundle;
72 import android.os.Environment;
73 import android.os.Handler;
74 import android.os.IBinder;
75 import android.os.Looper;
76 import android.os.Message;
77 import android.os.Parcel;
78 import android.os.Parcelable;
79 import android.os.Process;
80 import android.os.RemoteCallback;
81 import android.os.RemoteException;
82 import android.os.ResultReceiver;
83 import android.os.ShellCallback;
84 import android.os.StrictMode;
85 import android.os.SystemClock;
86 import android.os.UserHandle;
87 import android.os.UserManager;
88 import android.text.TextUtils;
89 import android.util.Log;
90 import android.util.Pair;
91 import android.util.Slog;
92 import android.util.SparseArray;
93 import android.util.SparseBooleanArray;
94 
95 import com.android.internal.R;
96 import com.android.internal.annotations.GuardedBy;
97 import com.android.internal.annotations.VisibleForTesting;
98 import com.android.internal.content.PackageMonitor;
99 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
100 import com.android.internal.notification.SystemNotificationChannels;
101 import com.android.internal.util.ArrayUtils;
102 import com.android.internal.util.DumpUtils;
103 import com.android.internal.util.IndentingPrintWriter;
104 import com.android.internal.util.Preconditions;
105 import com.android.server.LocalServices;
106 import com.android.server.ServiceThread;
107 import com.android.server.SystemService;
108 
109 import com.google.android.collect.Lists;
110 import com.google.android.collect.Sets;
111 
112 import java.io.File;
113 import java.io.FileDescriptor;
114 import java.io.PrintWriter;
115 import java.security.GeneralSecurityException;
116 import java.security.MessageDigest;
117 import java.security.NoSuchAlgorithmException;
118 import java.text.SimpleDateFormat;
119 import java.util.ArrayList;
120 import java.util.Arrays;
121 import java.util.Collection;
122 import java.util.Collections;
123 import java.util.Date;
124 import java.util.HashMap;
125 import java.util.HashSet;
126 import java.util.LinkedHashMap;
127 import java.util.List;
128 import java.util.Map;
129 import java.util.Map.Entry;
130 import java.util.Objects;
131 import java.util.Set;
132 import java.util.UUID;
133 import java.util.concurrent.CopyOnWriteArrayList;
134 import java.util.concurrent.atomic.AtomicReference;
135 
136 /**
137  * A system service that provides  account, password, and authtoken management for all
138  * accounts on the device. Some of these calls are implemented with the help of the corresponding
139  * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
140  * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
141  *    AccountManager accountManager = AccountManager.get(context);
142  * @hide
143  */
144 public class AccountManagerService
145         extends IAccountManager.Stub
146         implements RegisteredServicesCacheListener<AuthenticatorDescription> {
147     private static final String TAG = "AccountManagerService";
148 
149     public static class Lifecycle extends SystemService {
150         private AccountManagerService mService;
151 
Lifecycle(Context context)152         public Lifecycle(Context context) {
153             super(context);
154         }
155 
156         @Override
onStart()157         public void onStart() {
158             mService = new AccountManagerService(new Injector(getContext()));
159             publishBinderService(Context.ACCOUNT_SERVICE, mService);
160         }
161 
162         @Override
onUnlockUser(int userHandle)163         public void onUnlockUser(int userHandle) {
164             mService.onUnlockUser(userHandle);
165         }
166 
167         @Override
onStopUser(int userHandle)168         public void onStopUser(int userHandle) {
169             Slog.i(TAG, "onStopUser " + userHandle);
170             mService.purgeUserData(userHandle);
171         }
172     }
173 
174     final Context mContext;
175 
176     private final PackageManager mPackageManager;
177     private final AppOpsManager mAppOpsManager;
178     private UserManager mUserManager;
179     private final Injector mInjector;
180 
181     final MessageHandler mHandler;
182 
183     // Messages that can be sent on mHandler
184     private static final int MESSAGE_TIMED_OUT = 3;
185     private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
186 
187     private final IAccountAuthenticatorCache mAuthenticatorCache;
188     private static final String PRE_N_DATABASE_NAME = "accounts.db";
189     private static final Intent ACCOUNTS_CHANGED_INTENT;
190 
191     private static final int SIGNATURE_CHECK_MISMATCH = 0;
192     private static final int SIGNATURE_CHECK_MATCH = 1;
193     private static final int SIGNATURE_CHECK_UID_MATCH = 2;
194 
195     static {
196         ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
197         ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
198                 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
199     }
200 
201     private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
202 
203     static class UserAccounts {
204         private final int userId;
205         final AccountsDb accountsDb;
206         private final HashMap<Pair<Pair<Account, String>, Integer>, NotificationId>
207                 credentialsPermissionNotificationIds = new HashMap<>();
208         private final HashMap<Account, NotificationId> signinRequiredNotificationIds
209                 = new HashMap<>();
210         final Object cacheLock = new Object();
211         final Object dbLock = new Object(); // if needed, dbLock must be obtained before cacheLock
212         /** protected by the {@link #cacheLock} */
213         final HashMap<String, Account[]> accountCache = new LinkedHashMap<>();
214         /** protected by the {@link #cacheLock} */
215         private final Map<Account, Map<String, String>> userDataCache = new HashMap<>();
216         /** protected by the {@link #cacheLock} */
217         private final Map<Account, Map<String, String>> authTokenCache = new HashMap<>();
218         /** protected by the {@link #cacheLock} */
219         private final TokenCache accountTokenCaches = new TokenCache();
220         /** protected by the {@link #cacheLock} */
221         private final Map<Account, Map<String, Integer>> visibilityCache = new HashMap<>();
222 
223         /** protected by the {@link #mReceiversForType},
224          *  type -> (packageName -> number of active receivers)
225          *  type == null is used to get notifications about all account types
226          */
227         private final Map<String, Map<String, Integer>> mReceiversForType = new HashMap<>();
228 
229         /**
230          * protected by the {@link #cacheLock}
231          *
232          * Caches the previous names associated with an account. Previous names
233          * should be cached because we expect that when an Account is renamed,
234          * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
235          * want to know if the accounts they care about have been renamed.
236          *
237          * The previous names are wrapped in an {@link AtomicReference} so that
238          * we can distinguish between those accounts with no previous names and
239          * those whose previous names haven't been cached (yet).
240          */
241         private final HashMap<Account, AtomicReference<String>> previousNameCache =
242                 new HashMap<Account, AtomicReference<String>>();
243 
UserAccounts(Context context, int userId, File preNDbFile, File deDbFile)244         UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
245             this.userId = userId;
246             synchronized (dbLock) {
247                 synchronized (cacheLock) {
248                     accountsDb = AccountsDb.create(context, userId, preNDbFile, deDbFile);
249                 }
250             }
251         }
252     }
253 
254     private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
255     private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
256     // Not thread-safe. Only use in synchronized context
257     private final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
258     private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
259             mAppPermissionChangeListeners = new CopyOnWriteArrayList<>();
260 
261     private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
262     private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
263 
264     /**
265      * This should only be called by system code. One should only call this after the service
266      * has started.
267      * @return a reference to the AccountManagerService instance
268      * @hide
269      */
getSingleton()270     public static AccountManagerService getSingleton() {
271         return sThis.get();
272     }
273 
AccountManagerService(Injector injector)274     public AccountManagerService(Injector injector) {
275         mInjector = injector;
276         mContext = injector.getContext();
277         mPackageManager = mContext.getPackageManager();
278         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
279         mHandler = new MessageHandler(injector.getMessageHandlerLooper());
280         mAuthenticatorCache = mInjector.getAccountAuthenticatorCache();
281         mAuthenticatorCache.setListener(this, null /* Handler */);
282 
283         sThis.set(this);
284 
285         IntentFilter intentFilter = new IntentFilter();
286         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
287         intentFilter.addDataScheme("package");
288         mContext.registerReceiver(new BroadcastReceiver() {
289             @Override
290             public void onReceive(Context context1, Intent intent) {
291                 // Don't delete accounts when updating a authenticator's
292                 // package.
293                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
294                     /* Purging data requires file io, don't block the main thread. This is probably
295                      * less than ideal because we are introducing a race condition where old grants
296                      * could be exercised until they are purged. But that race condition existed
297                      * anyway with the broadcast receiver.
298                      *
299                      * Ideally, we would completely clear the cache, purge data from the database,
300                      * and then rebuild the cache. All under the cache lock. But that change is too
301                      * large at this point.
302                      */
303                     final String removedPackageName = intent.getData().getSchemeSpecificPart();
304                     Runnable purgingRunnable = new Runnable() {
305                         @Override
306                         public void run() {
307                             purgeOldGrantsAll();
308                             // Notify authenticator about removed app?
309                             removeVisibilityValuesForPackage(removedPackageName);
310                         }
311                     };
312                     mHandler.post(purgingRunnable);
313                 }
314             }
315         }, intentFilter);
316 
317         injector.addLocalService(new AccountManagerInternalImpl());
318 
319         IntentFilter userFilter = new IntentFilter();
320         userFilter.addAction(Intent.ACTION_USER_REMOVED);
321         mContext.registerReceiverAsUser(new BroadcastReceiver() {
322             @Override
323             public void onReceive(Context context, Intent intent) {
324                 String action = intent.getAction();
325                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
326                     int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
327                     if (userId < 1) return;
328                     Slog.i(TAG, "User " + userId + " removed");
329                     purgeUserData(userId);
330                 }
331             }
332         }, UserHandle.ALL, userFilter, null, null);
333 
334         // Need to cancel account request notifications if the update/install can access the account
335         new PackageMonitor() {
336             @Override
337             public void onPackageAdded(String packageName, int uid) {
338                 // Called on a handler, and running as the system
339                 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
340             }
341 
342             @Override
343             public void onPackageUpdateFinished(String packageName, int uid) {
344                 // Called on a handler, and running as the system
345                 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
346             }
347         }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
348 
349         // Cancel account request notification if an app op was preventing the account access
350         mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
351                 new AppOpsManager.OnOpChangedInternalListener() {
352             @Override
353             public void onOpChanged(int op, String packageName) {
354                 try {
355                     final int userId = ActivityManager.getCurrentUser();
356                     final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
357                     final int mode = mAppOpsManager.checkOpNoThrow(
358                             AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
359                     if (mode == AppOpsManager.MODE_ALLOWED) {
360                         final long identity = Binder.clearCallingIdentity();
361                         try {
362                             cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
363                         } finally {
364                             Binder.restoreCallingIdentity(identity);
365                         }
366                     }
367                 } catch (NameNotFoundException e) {
368                     /* ignore */
369                 }
370             }
371         });
372 
373         // Cancel account request notification if a permission was preventing the account access
374         mPackageManager.addOnPermissionsChangeListener(
375                 (int uid) -> {
376             Account[] accounts = null;
377             String[] packageNames = mPackageManager.getPackagesForUid(uid);
378             if (packageNames != null) {
379                 final int userId = UserHandle.getUserId(uid);
380                 final long identity = Binder.clearCallingIdentity();
381                 try {
382                     for (String packageName : packageNames) {
383                                 // if app asked for permission we need to cancel notification even
384                                 // for O+ applications.
385                                 if (mPackageManager.checkPermission(
386                                         Manifest.permission.GET_ACCOUNTS,
387                                         packageName) != PackageManager.PERMISSION_GRANTED) {
388                                     continue;
389                                 }
390 
391                         if (accounts == null) {
392                             accounts = getAccountsAsUser(null, userId, "android");
393                             if (ArrayUtils.isEmpty(accounts)) {
394                                 return;
395                             }
396                         }
397 
398                         for (Account account : accounts) {
399                             cancelAccountAccessRequestNotificationIfNeeded(
400                                     account, uid, packageName, true);
401                         }
402                     }
403                 } finally {
404                     Binder.restoreCallingIdentity(identity);
405                 }
406             }
407         });
408     }
409 
410 
getBindInstantServiceAllowed(int userId)411     boolean getBindInstantServiceAllowed(int userId) {
412         return  mAuthenticatorCache.getBindInstantServiceAllowed(userId);
413     }
414 
setBindInstantServiceAllowed(int userId, boolean allowed)415     void setBindInstantServiceAllowed(int userId, boolean allowed) {
416         mAuthenticatorCache.setBindInstantServiceAllowed(userId, allowed);
417     }
418 
cancelAccountAccessRequestNotificationIfNeeded(int uid, boolean checkAccess)419     private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
420             boolean checkAccess) {
421         Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
422         for (Account account : accounts) {
423             cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
424         }
425     }
426 
cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid, boolean checkAccess)427     private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
428             boolean checkAccess) {
429         Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
430         for (Account account : accounts) {
431             cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
432         }
433     }
434 
cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid, boolean checkAccess)435     private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
436             boolean checkAccess) {
437         String[] packageNames = mPackageManager.getPackagesForUid(uid);
438         if (packageNames != null) {
439             for (String packageName : packageNames) {
440                 cancelAccountAccessRequestNotificationIfNeeded(account, uid,
441                         packageName, checkAccess);
442             }
443         }
444     }
445 
cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid, String packageName, boolean checkAccess)446     private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
447             int uid, String packageName, boolean checkAccess) {
448         if (!checkAccess || hasAccountAccess(account, packageName,
449                 UserHandle.getUserHandleForUid(uid))) {
450             cancelNotification(getCredentialPermissionNotificationId(account,
451                     AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
452                     UserHandle.getUserHandleForUid(uid));
453         }
454     }
455 
456     @Override
addAccountExplicitlyWithVisibility(Account account, String password, Bundle extras, Map packageToVisibility)457     public boolean addAccountExplicitlyWithVisibility(Account account, String password,
458             Bundle extras, Map packageToVisibility) {
459         Bundle.setDefusable(extras, true);
460         int callingUid = Binder.getCallingUid();
461         int userId = UserHandle.getCallingUserId();
462         if (Log.isLoggable(TAG, Log.VERBOSE)) {
463             Log.v(TAG, "addAccountExplicitly: " + account + ", caller's uid " + callingUid
464                     + ", pid " + Binder.getCallingPid());
465         }
466         Preconditions.checkNotNull(account, "account cannot be null");
467         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
468             String msg = String.format("uid %s cannot explicitly add accounts of type: %s",
469                     callingUid, account.type);
470             throw new SecurityException(msg);
471         }
472         /*
473          * Child users are not allowed to add accounts. Only the accounts that are shared by the
474          * parent profile can be added to child profile.
475          *
476          * TODO: Only allow accounts that were shared to be added by a limited user.
477          */
478         // fails if the account already exists
479         long identityToken = clearCallingIdentity();
480         try {
481             UserAccounts accounts = getUserAccounts(userId);
482             return addAccountInternal(accounts, account, password, extras, callingUid,
483                     (Map<String, Integer>) packageToVisibility);
484         } finally {
485             restoreCallingIdentity(identityToken);
486         }
487     }
488 
489     @Override
getAccountsAndVisibilityForPackage(String packageName, String accountType)490     public Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
491             String accountType) {
492         int callingUid = Binder.getCallingUid();
493         int userId = UserHandle.getCallingUserId();
494         boolean isSystemUid = UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
495         List<String> managedTypes = getTypesForCaller(callingUid, userId, isSystemUid);
496 
497         if ((accountType != null && !managedTypes.contains(accountType))
498                 || (accountType == null && !isSystemUid)) {
499             throw new SecurityException(
500                     "getAccountsAndVisibilityForPackage() called from unauthorized uid "
501                             + callingUid + " with packageName=" + packageName);
502         }
503         if (accountType != null) {
504             managedTypes = new ArrayList<String>();
505             managedTypes.add(accountType);
506         }
507 
508         long identityToken = clearCallingIdentity();
509         try {
510             UserAccounts accounts = getUserAccounts(userId);
511             return getAccountsAndVisibilityForPackage(packageName, managedTypes, callingUid,
512                     accounts);
513         } finally {
514             restoreCallingIdentity(identityToken);
515         }
516     }
517 
518     /*
519      * accountTypes may not be null
520      */
getAccountsAndVisibilityForPackage(String packageName, List<String> accountTypes, Integer callingUid, UserAccounts accounts)521     private Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
522             List<String> accountTypes, Integer callingUid, UserAccounts accounts) {
523         if (!packageExistsForUser(packageName, accounts.userId)) {
524             Log.d(TAG, "Package not found " + packageName);
525             return new LinkedHashMap<>();
526         }
527 
528         Map<Account, Integer> result = new LinkedHashMap<>();
529         for (String accountType : accountTypes) {
530             synchronized (accounts.dbLock) {
531                 synchronized (accounts.cacheLock) {
532                     final Account[] accountsOfType = accounts.accountCache.get(accountType);
533                     if (accountsOfType != null) {
534                         for (Account account : accountsOfType) {
535                             result.put(account,
536                                     resolveAccountVisibility(account, packageName, accounts));
537                         }
538                     }
539                 }
540             }
541         }
542         return filterSharedAccounts(accounts, result, callingUid, packageName);
543     }
544 
545     @Override
getPackagesAndVisibilityForAccount(Account account)546     public Map<String, Integer> getPackagesAndVisibilityForAccount(Account account) {
547         Preconditions.checkNotNull(account, "account cannot be null");
548         int callingUid = Binder.getCallingUid();
549         int userId = UserHandle.getCallingUserId();
550         if (!isAccountManagedByCaller(account.type, callingUid, userId)
551                 && !isSystemUid(callingUid)) {
552             String msg =
553                     String.format("uid %s cannot get secrets for account %s", callingUid, account);
554             throw new SecurityException(msg);
555         }
556 
557         long identityToken = clearCallingIdentity();
558         try {
559             UserAccounts accounts = getUserAccounts(userId);
560             synchronized (accounts.dbLock) {
561                 synchronized (accounts.cacheLock) {
562                     return getPackagesAndVisibilityForAccountLocked(account, accounts);
563                 }
564             }
565         } finally {
566             restoreCallingIdentity(identityToken);
567         }
568 
569     }
570 
571     /**
572      * Returns Map with all package names and visibility values for given account.
573      * The method and returned map must be guarded by accounts.cacheLock
574      *
575      * @param account Account to get visibility values.
576      * @param accounts UserAccount that currently hosts the account and application
577      *
578      * @return Map with cache for package names to visibility.
579      */
getPackagesAndVisibilityForAccountLocked(Account account, UserAccounts accounts)580     private @NonNull Map<String, Integer> getPackagesAndVisibilityForAccountLocked(Account account,
581             UserAccounts accounts) {
582         Map<String, Integer> accountVisibility = accounts.visibilityCache.get(account);
583         if (accountVisibility == null) {
584             Log.d(TAG, "Visibility was not initialized");
585             accountVisibility = new HashMap<>();
586             accounts.visibilityCache.put(account, accountVisibility);
587         }
588         return accountVisibility;
589     }
590 
591     @Override
getAccountVisibility(Account account, String packageName)592     public int getAccountVisibility(Account account, String packageName) {
593         Preconditions.checkNotNull(account, "account cannot be null");
594         Preconditions.checkNotNull(packageName, "packageName cannot be null");
595         int callingUid = Binder.getCallingUid();
596         int userId = UserHandle.getCallingUserId();
597         if (!isAccountManagedByCaller(account.type, callingUid, userId)
598             && !isSystemUid(callingUid)) {
599             String msg = String.format(
600                     "uid %s cannot get secrets for accounts of type: %s",
601                     callingUid,
602                     account.type);
603             throw new SecurityException(msg);
604         }
605         long identityToken = clearCallingIdentity();
606         try {
607             UserAccounts accounts = getUserAccounts(userId);
608             if (AccountManager.PACKAGE_NAME_KEY_LEGACY_VISIBLE.equals(packageName)) {
609                 int visibility = getAccountVisibilityFromCache(account, packageName, accounts);
610                 if (AccountManager.VISIBILITY_UNDEFINED != visibility) {
611                     return visibility;
612                 } else {
613                    return AccountManager.VISIBILITY_USER_MANAGED_VISIBLE;
614                 }
615             }
616             if (AccountManager.PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE.equals(packageName)) {
617                 int visibility = getAccountVisibilityFromCache(account, packageName, accounts);
618                 if (AccountManager.VISIBILITY_UNDEFINED != visibility) {
619                     return visibility;
620                 } else {
621                    return AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE;
622                 }
623             }
624             return resolveAccountVisibility(account, packageName, accounts);
625         } finally {
626             restoreCallingIdentity(identityToken);
627         }
628     }
629 
630     /**
631      * Method returns visibility for given account and package name.
632      *
633      * @param account The account to check visibility.
634      * @param packageName Package name to check visibility.
635      * @param accounts UserAccount that currently hosts the account and application
636      *
637      * @return Visibility value, AccountManager.VISIBILITY_UNDEFINED if no value was stored.
638      *
639      */
getAccountVisibilityFromCache(Account account, String packageName, UserAccounts accounts)640     private int getAccountVisibilityFromCache(Account account, String packageName,
641             UserAccounts accounts) {
642         synchronized (accounts.cacheLock) {
643             Map<String, Integer> accountVisibility =
644                     getPackagesAndVisibilityForAccountLocked(account, accounts);
645             Integer visibility = accountVisibility.get(packageName);
646             return visibility != null ? visibility : AccountManager.VISIBILITY_UNDEFINED;
647         }
648     }
649 
650     /**
651      * Method which handles default values for Account visibility.
652      *
653      * @param account The account to check visibility.
654      * @param packageName Package name to check visibility
655      * @param accounts UserAccount that currently hosts the account and application
656      *
657      * @return Visibility value, the method never returns AccountManager.VISIBILITY_UNDEFINED
658      *
659      */
resolveAccountVisibility(Account account, @NonNull String packageName, UserAccounts accounts)660     private Integer resolveAccountVisibility(Account account, @NonNull String packageName,
661             UserAccounts accounts) {
662         Preconditions.checkNotNull(packageName, "packageName cannot be null");
663         int uid = -1;
664         try {
665             long identityToken = clearCallingIdentity();
666             try {
667                 uid = mPackageManager.getPackageUidAsUser(packageName, accounts.userId);
668             } finally {
669                 restoreCallingIdentity(identityToken);
670             }
671         } catch (NameNotFoundException e) {
672             Log.d(TAG, "Package not found " + e.getMessage());
673             return AccountManager.VISIBILITY_NOT_VISIBLE;
674         }
675 
676         // System visibility can not be restricted.
677         if (UserHandle.isSameApp(uid, Process.SYSTEM_UID)) {
678             return AccountManager.VISIBILITY_VISIBLE;
679         }
680 
681         int signatureCheckResult =
682                 checkPackageSignature(account.type, uid, accounts.userId);
683 
684         // Authenticator can not restrict visibility to itself.
685         if (signatureCheckResult == SIGNATURE_CHECK_UID_MATCH) {
686             return AccountManager.VISIBILITY_VISIBLE; // Authenticator can always see the account
687         }
688 
689         // Return stored value if it was set.
690         int visibility = getAccountVisibilityFromCache(account, packageName, accounts);
691 
692         if (AccountManager.VISIBILITY_UNDEFINED != visibility) {
693             return visibility;
694         }
695 
696         boolean isPrivileged = isPermittedForPackage(packageName, accounts.userId,
697                 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
698 
699         // Device/Profile owner gets visibility by default.
700         if (isProfileOwner(uid)) {
701             return AccountManager.VISIBILITY_VISIBLE;
702         }
703 
704         boolean preO = isPreOApplication(packageName);
705         if ((signatureCheckResult != SIGNATURE_CHECK_MISMATCH)
706                 || (preO && checkGetAccountsPermission(packageName, accounts.userId))
707                 || (checkReadContactsPermission(packageName, accounts.userId)
708                     && accountTypeManagesContacts(account.type, accounts.userId))
709                 || isPrivileged) {
710             // Use legacy for preO apps with GET_ACCOUNTS permission or pre/postO with signature
711             // match.
712             visibility = getAccountVisibilityFromCache(account,
713                     AccountManager.PACKAGE_NAME_KEY_LEGACY_VISIBLE, accounts);
714             if (AccountManager.VISIBILITY_UNDEFINED == visibility) {
715                 visibility = AccountManager.VISIBILITY_USER_MANAGED_VISIBLE;
716             }
717         } else {
718             visibility = getAccountVisibilityFromCache(account,
719                     AccountManager.PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE, accounts);
720             if (AccountManager.VISIBILITY_UNDEFINED == visibility) {
721                 visibility = AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE;
722             }
723         }
724         return visibility;
725     }
726 
727     /**
728      * Checks targetSdk for a package;
729      *
730      * @param packageName Package name
731      *
732      * @return True if package's target SDK is below {@link android.os.Build.VERSION_CODES#O}, or
733      *         undefined
734      */
isPreOApplication(String packageName)735     private boolean isPreOApplication(String packageName) {
736         try {
737             long identityToken = clearCallingIdentity();
738             ApplicationInfo applicationInfo;
739             try {
740                 applicationInfo = mPackageManager.getApplicationInfo(packageName, 0);
741             } finally {
742                 restoreCallingIdentity(identityToken);
743             }
744 
745             if (applicationInfo != null) {
746                 int version = applicationInfo.targetSdkVersion;
747                 return version < android.os.Build.VERSION_CODES.O;
748             }
749             return true;
750         } catch (NameNotFoundException e) {
751             Log.d(TAG, "Package not found " + e.getMessage());
752             return true;
753         }
754     }
755 
756     @Override
setAccountVisibility(Account account, String packageName, int newVisibility)757     public boolean setAccountVisibility(Account account, String packageName, int newVisibility) {
758         Preconditions.checkNotNull(account, "account cannot be null");
759         Preconditions.checkNotNull(packageName, "packageName cannot be null");
760         int callingUid = Binder.getCallingUid();
761         int userId = UserHandle.getCallingUserId();
762         if (!isAccountManagedByCaller(account.type, callingUid, userId)
763             && !isSystemUid(callingUid)) {
764             String msg = String.format(
765                     "uid %s cannot get secrets for accounts of type: %s",
766                     callingUid,
767                     account.type);
768             throw new SecurityException(msg);
769         }
770         long identityToken = clearCallingIdentity();
771         try {
772             UserAccounts accounts = getUserAccounts(userId);
773             return setAccountVisibility(account, packageName, newVisibility, true /* notify */,
774                 accounts);
775         } finally {
776             restoreCallingIdentity(identityToken);
777         }
778     }
779 
isVisible(int visibility)780     private boolean isVisible(int visibility) {
781         return visibility == AccountManager.VISIBILITY_VISIBLE ||
782             visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE;
783     }
784 
785     /**
786      * Updates visibility for given account name and package.
787      *
788      * @param account Account to update visibility.
789      * @param packageName Package name for which visibility is updated.
790      * @param newVisibility New visibility calue
791      * @param notify if the flag is set applications will get notification about visibility change
792      * @param accounts UserAccount that currently hosts the account and application
793      *
794      * @return True if account visibility was changed.
795      */
setAccountVisibility(Account account, String packageName, int newVisibility, boolean notify, UserAccounts accounts)796     private boolean setAccountVisibility(Account account, String packageName, int newVisibility,
797             boolean notify, UserAccounts accounts) {
798         synchronized (accounts.dbLock) {
799             synchronized (accounts.cacheLock) {
800                 Map<String, Integer> packagesToVisibility;
801                 List<String> accountRemovedReceivers;
802                 if (notify) {
803                     if (isSpecialPackageKey(packageName)) {
804                         packagesToVisibility =
805                                 getRequestingPackages(account, accounts);
806                         accountRemovedReceivers = getAccountRemovedReceivers(account, accounts);
807                     } else {
808                         if (!packageExistsForUser(packageName, accounts.userId)) {
809                             return false; // package is not installed.
810                         }
811                         packagesToVisibility = new HashMap<>();
812                         packagesToVisibility.put(packageName,
813                                 resolveAccountVisibility(account, packageName, accounts));
814                         accountRemovedReceivers = new ArrayList<>();
815                         if (shouldNotifyPackageOnAccountRemoval(account, packageName, accounts)) {
816                             accountRemovedReceivers.add(packageName);
817                         }
818                     }
819                 } else {
820                     // Notifications will not be send - only used during add account.
821                     if (!isSpecialPackageKey(packageName) &&
822                             !packageExistsForUser(packageName, accounts.userId)) {
823                         // package is not installed and not meta value.
824                         return false;
825                     }
826                     packagesToVisibility = Collections.emptyMap();
827                     accountRemovedReceivers = Collections.emptyList();
828                 }
829 
830                 if (!updateAccountVisibilityLocked(account, packageName, newVisibility, accounts)) {
831                     return false;
832                 }
833 
834                 if (notify) {
835                     for (Entry<String, Integer> packageToVisibility : packagesToVisibility
836                             .entrySet()) {
837                         int oldVisibility = packageToVisibility.getValue();
838                         int currentVisibility =
839                             resolveAccountVisibility(account, packageName, accounts);
840                         if (isVisible(oldVisibility) != isVisible(currentVisibility)) {
841                             notifyPackage(packageToVisibility.getKey(), accounts);
842                         }
843                     }
844                     for (String packageNameToNotify : accountRemovedReceivers) {
845                         sendAccountRemovedBroadcast(account, packageNameToNotify, accounts.userId);
846                     }
847                     sendAccountsChangedBroadcast(accounts.userId);
848                 }
849                 return true;
850             }
851         }
852     }
853 
854     // Update account visibility in cache and database.
updateAccountVisibilityLocked(Account account, String packageName, int newVisibility, UserAccounts accounts)855     private boolean updateAccountVisibilityLocked(Account account, String packageName,
856             int newVisibility, UserAccounts accounts) {
857         final long accountId = accounts.accountsDb.findDeAccountId(account);
858         if (accountId < 0) {
859             return false;
860         }
861 
862         final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
863         try {
864             if (!accounts.accountsDb.setAccountVisibility(accountId, packageName,
865                     newVisibility)) {
866                 return false;
867             }
868         } finally {
869             StrictMode.setThreadPolicy(oldPolicy);
870         }
871         Map<String, Integer> accountVisibility =
872             getPackagesAndVisibilityForAccountLocked(account, accounts);
873         accountVisibility.put(packageName, newVisibility);
874         return true;
875     }
876 
877     @Override
registerAccountListener(String[] accountTypes, String opPackageName)878     public void registerAccountListener(String[] accountTypes, String opPackageName) {
879         int callingUid = Binder.getCallingUid();
880         mAppOpsManager.checkPackage(callingUid, opPackageName);
881 
882         int userId = UserHandle.getCallingUserId();
883         long identityToken = clearCallingIdentity();
884         try {
885             UserAccounts accounts = getUserAccounts(userId);
886             registerAccountListener(accountTypes, opPackageName, accounts);
887         } finally {
888             restoreCallingIdentity(identityToken);
889         }
890     }
891 
registerAccountListener(String[] accountTypes, String opPackageName, UserAccounts accounts)892     private void registerAccountListener(String[] accountTypes, String opPackageName,
893             UserAccounts accounts) {
894         synchronized (accounts.mReceiversForType) {
895             if (accountTypes == null) {
896                 // null for any type
897                 accountTypes = new String[] {null};
898             }
899             for (String type : accountTypes) {
900                 Map<String, Integer> receivers = accounts.mReceiversForType.get(type);
901                 if (receivers == null) {
902                     receivers = new HashMap<>();
903                     accounts.mReceiversForType.put(type, receivers);
904                 }
905                 Integer cnt = receivers.get(opPackageName);
906                 receivers.put(opPackageName, cnt != null ? cnt + 1 : 1);
907             }
908         }
909     }
910 
911     @Override
unregisterAccountListener(String[] accountTypes, String opPackageName)912     public void unregisterAccountListener(String[] accountTypes, String opPackageName) {
913         int callingUid = Binder.getCallingUid();
914         mAppOpsManager.checkPackage(callingUid, opPackageName);
915         int userId = UserHandle.getCallingUserId();
916         long identityToken = clearCallingIdentity();
917         try {
918             UserAccounts accounts = getUserAccounts(userId);
919             unregisterAccountListener(accountTypes, opPackageName, accounts);
920         } finally {
921             restoreCallingIdentity(identityToken);
922         }
923     }
924 
unregisterAccountListener(String[] accountTypes, String opPackageName, UserAccounts accounts)925     private void unregisterAccountListener(String[] accountTypes, String opPackageName,
926             UserAccounts accounts) {
927         synchronized (accounts.mReceiversForType) {
928             if (accountTypes == null) {
929                 // null for any type
930                 accountTypes = new String[] {null};
931             }
932             for (String type : accountTypes) {
933                 Map<String, Integer> receivers = accounts.mReceiversForType.get(type);
934                 if (receivers == null || receivers.get(opPackageName) == null) {
935                     throw new IllegalArgumentException("attempt to unregister wrong receiver");
936                 }
937                 Integer cnt = receivers.get(opPackageName);
938                 if (cnt == 1) {
939                     receivers.remove(opPackageName);
940                 } else {
941                     receivers.put(opPackageName, cnt - 1);
942                 }
943             }
944         }
945     }
946 
947     // Send notification to all packages which can potentially see the account
sendNotificationAccountUpdated(Account account, UserAccounts accounts)948     private void sendNotificationAccountUpdated(Account account, UserAccounts accounts) {
949         Map<String, Integer> packagesToVisibility = getRequestingPackages(account, accounts);
950 
951         for (Entry<String, Integer> packageToVisibility : packagesToVisibility.entrySet()) {
952             if ((packageToVisibility.getValue() != AccountManager.VISIBILITY_NOT_VISIBLE)
953                     && (packageToVisibility.getValue()
954                         != AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE)) {
955                 notifyPackage(packageToVisibility.getKey(), accounts);
956             }
957         }
958     }
959 
960     /**
961      * Sends a direct intent to a package, notifying it of account visibility change.
962      *
963      * @param packageName to send Account to
964      * @param accounts UserAccount that currently hosts the account
965      */
notifyPackage(String packageName, UserAccounts accounts)966     private void notifyPackage(String packageName, UserAccounts accounts) {
967         Intent intent = new Intent(AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED);
968         intent.setPackage(packageName);
969         intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
970         mContext.sendBroadcastAsUser(intent, new UserHandle(accounts.userId));
971     }
972 
973     // Returns a map from package name to visibility, for packages subscribed
974     // to notifications about any account type, or type of provided account
975     // account type or all types.
getRequestingPackages(Account account, UserAccounts accounts)976     private Map<String, Integer> getRequestingPackages(Account account, UserAccounts accounts) {
977         Set<String> packages = new HashSet<>();
978         synchronized (accounts.mReceiversForType) {
979             for (String type : new String[] {account.type, null}) {
980                 Map<String, Integer> receivers = accounts.mReceiversForType.get(type);
981                 if (receivers != null) {
982                     packages.addAll(receivers.keySet());
983                 }
984             }
985         }
986         Map<String, Integer> result = new HashMap<>();
987         for (String packageName : packages) {
988             result.put(packageName, resolveAccountVisibility(account, packageName, accounts));
989         }
990         return result;
991     }
992 
993     // Returns a list of packages listening to ACTION_ACCOUNT_REMOVED able to see the account.
getAccountRemovedReceivers(Account account, UserAccounts accounts)994     private List<String> getAccountRemovedReceivers(Account account, UserAccounts accounts) {
995         Intent intent = new Intent(AccountManager.ACTION_ACCOUNT_REMOVED);
996         intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
997         List<ResolveInfo> receivers =
998             mPackageManager.queryBroadcastReceiversAsUser(intent, 0, accounts.userId);
999         List<String> result = new ArrayList<>();
1000         if (receivers == null) {
1001             return result;
1002         }
1003         for (ResolveInfo resolveInfo: receivers) {
1004             String packageName = resolveInfo.activityInfo.applicationInfo.packageName;
1005             int visibility = resolveAccountVisibility(account, packageName, accounts);
1006             if (visibility == AccountManager.VISIBILITY_VISIBLE
1007                 || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE) {
1008                 result.add(packageName);
1009             }
1010         }
1011         return result;
1012     }
1013 
1014     // Returns true if given package is listening to ACTION_ACCOUNT_REMOVED and can see the account.
shouldNotifyPackageOnAccountRemoval(Account account, String packageName, UserAccounts accounts)1015     private boolean shouldNotifyPackageOnAccountRemoval(Account account,
1016             String packageName, UserAccounts accounts) {
1017         int visibility = resolveAccountVisibility(account, packageName, accounts);
1018         if (visibility != AccountManager.VISIBILITY_VISIBLE
1019             && visibility != AccountManager.VISIBILITY_USER_MANAGED_VISIBLE) {
1020             return false;
1021         }
1022 
1023         Intent intent = new Intent(AccountManager.ACTION_ACCOUNT_REMOVED);
1024         intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1025         intent.setPackage(packageName);
1026         List<ResolveInfo> receivers =
1027             mPackageManager.queryBroadcastReceiversAsUser(intent, 0, accounts.userId);
1028         return (receivers != null && receivers.size() > 0);
1029     }
1030 
packageExistsForUser(String packageName, int userId)1031     private boolean packageExistsForUser(String packageName, int userId) {
1032         try {
1033             long identityToken = clearCallingIdentity();
1034             try {
1035                 mPackageManager.getPackageUidAsUser(packageName, userId);
1036                 return true;
1037             } finally {
1038                 restoreCallingIdentity(identityToken);
1039             }
1040         } catch (NameNotFoundException e) {
1041             return false;
1042         }
1043     }
1044 
1045     /**
1046      * Returns true if packageName is one of special values.
1047      */
isSpecialPackageKey(String packageName)1048     private boolean isSpecialPackageKey(String packageName) {
1049         return (AccountManager.PACKAGE_NAME_KEY_LEGACY_VISIBLE.equals(packageName)
1050                 || AccountManager.PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE.equals(packageName));
1051     }
1052 
sendAccountsChangedBroadcast(int userId)1053     private void sendAccountsChangedBroadcast(int userId) {
1054         Log.i(TAG, "the accounts changed, sending broadcast of "
1055                 + ACCOUNTS_CHANGED_INTENT.getAction());
1056         mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
1057     }
1058 
sendAccountRemovedBroadcast(Account account, String packageName, int userId)1059     private void sendAccountRemovedBroadcast(Account account, String packageName, int userId) {
1060         Intent intent = new Intent(AccountManager.ACTION_ACCOUNT_REMOVED);
1061         intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
1062         intent.setPackage(packageName);
1063         intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
1064         intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, account.type);
1065         mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
1066     }
1067 
1068     @Override
onTransact(int code, Parcel data, Parcel reply, int flags)1069     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
1070             throws RemoteException {
1071         try {
1072             return super.onTransact(code, data, reply, flags);
1073         } catch (RuntimeException e) {
1074             // The account manager only throws security exceptions, so let's
1075             // log all others.
1076             if (!(e instanceof SecurityException)) {
1077                 Slog.wtf(TAG, "Account Manager Crash", e);
1078             }
1079             throw e;
1080         }
1081     }
1082 
getUserManager()1083     private UserManager getUserManager() {
1084         if (mUserManager == null) {
1085             mUserManager = UserManager.get(mContext);
1086         }
1087         return mUserManager;
1088     }
1089 
1090     /**
1091      * Validate internal set of accounts against installed authenticators for
1092      * given user. Clears cached authenticators before validating.
1093      */
validateAccounts(int userId)1094     public void validateAccounts(int userId) {
1095         final UserAccounts accounts = getUserAccounts(userId);
1096         // Invalidate user-specific cache to make sure we catch any
1097         // removed authenticators.
1098         validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
1099     }
1100 
1101     /**
1102      * Validate internal set of accounts against installed authenticators for
1103      * given user. Clear cached authenticators before validating when requested.
1104      */
validateAccountsInternal( UserAccounts accounts, boolean invalidateAuthenticatorCache)1105     private void validateAccountsInternal(
1106             UserAccounts accounts, boolean invalidateAuthenticatorCache) {
1107         if (Log.isLoggable(TAG, Log.DEBUG)) {
1108             Log.d(TAG, "validateAccountsInternal " + accounts.userId
1109                     + " isCeDatabaseAttached=" + accounts.accountsDb.isCeDatabaseAttached()
1110                     + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
1111         }
1112 
1113         if (invalidateAuthenticatorCache) {
1114             mAuthenticatorCache.invalidateCache(accounts.userId);
1115         }
1116 
1117         final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
1118                 mAuthenticatorCache, accounts.userId);
1119         boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
1120 
1121         synchronized (accounts.dbLock) {
1122             synchronized (accounts.cacheLock) {
1123                 boolean accountDeleted = false;
1124 
1125                 // Get a map of stored authenticator types to UID
1126                 final AccountsDb accountsDb = accounts.accountsDb;
1127                 Map<String, Integer> metaAuthUid = accountsDb.findMetaAuthUid();
1128                 // Create a list of authenticator type whose previous uid no longer exists
1129                 HashSet<String> obsoleteAuthType = Sets.newHashSet();
1130                 SparseBooleanArray knownUids = null;
1131                 for (Entry<String, Integer> authToUidEntry : metaAuthUid.entrySet()) {
1132                     String type = authToUidEntry.getKey();
1133                     int uid = authToUidEntry.getValue();
1134                     Integer knownUid = knownAuth.get(type);
1135                     if (knownUid != null && uid == knownUid) {
1136                         // Remove it from the knownAuth list if it's unchanged.
1137                         knownAuth.remove(type);
1138                     } else {
1139                     /*
1140                      * The authenticator is presently not cached and should only be triggered
1141                      * when we think an authenticator has been removed (or is being updated).
1142                      * But we still want to check if any data with the associated uid is
1143                      * around. This is an (imperfect) signal that the package may be updating.
1144                      *
1145                      * A side effect of this is that an authenticator sharing a uid with
1146                      * multiple apps won't get its credentials wiped as long as some app with
1147                      * that uid is still on the device. But I suspect that this is a rare case.
1148                      * And it isn't clear to me how an attacker could really exploit that
1149                      * feature.
1150                      *
1151                      * The upshot is that we don't have to worry about accounts getting
1152                      * uninstalled while the authenticator's package is being updated.
1153                      *
1154                      */
1155                         if (knownUids == null) {
1156                             knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
1157                         }
1158                         if (!knownUids.get(uid)) {
1159                             // The authenticator is not presently available to the cache. And the
1160                             // package no longer has a data directory (so we surmise it isn't
1161                             // updating). So purge its data from the account databases.
1162                             obsoleteAuthType.add(type);
1163                             // And delete it from the TABLE_META
1164                             accountsDb.deleteMetaByAuthTypeAndUid(type, uid);
1165                         }
1166                     }
1167                 }
1168 
1169                 // Add the newly registered authenticator to TABLE_META. If old authenticators have
1170                 // been re-enabled (after being updated for example), then we just overwrite the old
1171                 // values.
1172                 for (Entry<String, Integer> entry : knownAuth.entrySet()) {
1173                     accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue());
1174                 }
1175 
1176                 final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts();
1177                 try {
1178                     accounts.accountCache.clear();
1179                     final HashMap<String, ArrayList<String>> accountNamesByType
1180                             = new LinkedHashMap<>();
1181                     for (Entry<Long, Account> accountEntry : accountsMap.entrySet()) {
1182                         final long accountId = accountEntry.getKey();
1183                         final Account account = accountEntry.getValue();
1184                         if (obsoleteAuthType.contains(account.type)) {
1185                             Slog.w(TAG, "deleting account " + account.toSafeString()
1186                                     + " because type " + account.type
1187                                     + "'s registered authenticator no longer exist.");
1188                             Map<String, Integer> packagesToVisibility =
1189                                     getRequestingPackages(account, accounts);
1190                             List<String> accountRemovedReceivers =
1191                                 getAccountRemovedReceivers(account, accounts);
1192                             accountsDb.beginTransaction();
1193                             try {
1194                                 accountsDb.deleteDeAccount(accountId);
1195                                 // Also delete from CE table if user is unlocked; if user is
1196                                 // currently locked the account will be removed later by
1197                                 // syncDeCeAccountsLocked
1198                                 if (userUnlocked) {
1199                                     accountsDb.deleteCeAccount(accountId);
1200                                 }
1201                                 accountsDb.setTransactionSuccessful();
1202                             } finally {
1203                                 accountsDb.endTransaction();
1204                             }
1205                             accountDeleted = true;
1206 
1207                             logRecord(AccountsDb.DEBUG_ACTION_AUTHENTICATOR_REMOVE,
1208                                     AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
1209 
1210                             accounts.userDataCache.remove(account);
1211                             accounts.authTokenCache.remove(account);
1212                             accounts.accountTokenCaches.remove(account);
1213                             accounts.visibilityCache.remove(account);
1214 
1215                             for (Entry<String, Integer> packageToVisibility :
1216                                     packagesToVisibility.entrySet()) {
1217                                 if (isVisible(packageToVisibility.getValue())) {
1218                                     notifyPackage(packageToVisibility.getKey(), accounts);
1219                                 }
1220                             }
1221                             for (String packageName : accountRemovedReceivers) {
1222                                 sendAccountRemovedBroadcast(account, packageName, accounts.userId);
1223                             }
1224                         } else {
1225                             ArrayList<String> accountNames = accountNamesByType.get(account.type);
1226                             if (accountNames == null) {
1227                                 accountNames = new ArrayList<>();
1228                                 accountNamesByType.put(account.type, accountNames);
1229                             }
1230                             accountNames.add(account.name);
1231                         }
1232                     }
1233                     for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
1234                         final String accountType = cur.getKey();
1235                         final ArrayList<String> accountNames = cur.getValue();
1236                         final Account[] accountsForType = new Account[accountNames.size()];
1237                         for (int i = 0; i < accountsForType.length; i++) {
1238                             accountsForType[i] = new Account(accountNames.get(i), accountType,
1239                                     UUID.randomUUID().toString());
1240                         }
1241                         accounts.accountCache.put(accountType, accountsForType);
1242                     }
1243                     accounts.visibilityCache.putAll(accountsDb.findAllVisibilityValues());
1244                 } finally {
1245                     if (accountDeleted) {
1246                         sendAccountsChangedBroadcast(accounts.userId);
1247                     }
1248                 }
1249             }
1250         }
1251     }
1252 
getUidsOfInstalledOrUpdatedPackagesAsUser(int userId)1253     private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
1254         // Get the UIDs of all apps that might have data on the device. We want
1255         // to preserve user data if the app might otherwise be storing data.
1256         List<PackageInfo> pkgsWithData =
1257                 mPackageManager.getInstalledPackagesAsUser(
1258                         PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
1259         SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
1260         for (PackageInfo pkgInfo : pkgsWithData) {
1261             if (pkgInfo.applicationInfo != null
1262                     && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
1263                 knownUids.put(pkgInfo.applicationInfo.uid, true);
1264             }
1265         }
1266         return knownUids;
1267     }
1268 
getAuthenticatorTypeAndUIDForUser( Context context, int userId)1269     static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1270             Context context,
1271             int userId) {
1272         AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
1273         return getAuthenticatorTypeAndUIDForUser(authCache, userId);
1274     }
1275 
getAuthenticatorTypeAndUIDForUser( IAccountAuthenticatorCache authCache, int userId)1276     private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1277             IAccountAuthenticatorCache authCache,
1278             int userId) {
1279         HashMap<String, Integer> knownAuth = new LinkedHashMap<>();
1280         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
1281                 .getAllServices(userId)) {
1282             knownAuth.put(service.type.type, service.uid);
1283         }
1284         return knownAuth;
1285     }
1286 
getUserAccountsForCaller()1287     private UserAccounts getUserAccountsForCaller() {
1288         return getUserAccounts(UserHandle.getCallingUserId());
1289     }
1290 
getUserAccounts(int userId)1291     protected UserAccounts getUserAccounts(int userId) {
1292         try {
1293             return getUserAccountsNotChecked(userId);
1294         } catch (RuntimeException e) {
1295             if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
1296                 // Let it go...
1297                 throw e;
1298             }
1299             // User accounts database is corrupted, we must wipe out the whole user, otherwise the
1300             // system will crash indefinitely
1301             Slog.wtf(TAG, "Removing user " + userId + " due to exception (" + e + ") reading its "
1302                     + "account database");
1303             if (userId == ActivityManager.getCurrentUser() && userId != UserHandle.USER_SYSTEM) {
1304                 Slog.i(TAG, "Switching to system user first");
1305                 try {
1306                     ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
1307                 } catch (RemoteException re) {
1308                     Slog.e(TAG, "Could not switch to " + UserHandle.USER_SYSTEM + ": " + re);
1309                 }
1310             }
1311             if (!getUserManager().removeUserEvenWhenDisallowed(userId)) {
1312                 Slog.e(TAG, "could not remove user " + userId);
1313             }
1314             throw e;
1315         }
1316     }
1317 
getUserAccountsNotChecked(int userId)1318     private UserAccounts getUserAccountsNotChecked(int userId) {
1319         synchronized (mUsers) {
1320             UserAccounts accounts = mUsers.get(userId);
1321             boolean validateAccounts = false;
1322             if (accounts == null) {
1323                 File preNDbFile = new File(mInjector.getPreNDatabaseName(userId));
1324                 File deDbFile = new File(mInjector.getDeDatabaseName(userId));
1325                 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
1326                 mUsers.append(userId, accounts);
1327                 purgeOldGrants(accounts);
1328                 validateAccounts = true;
1329             }
1330             // open CE database if necessary
1331             if (!accounts.accountsDb.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
1332                 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
1333                 synchronized (accounts.dbLock) {
1334                     synchronized (accounts.cacheLock) {
1335                         File ceDatabaseFile = new File(mInjector.getCeDatabaseName(userId));
1336                         accounts.accountsDb.attachCeDatabase(ceDatabaseFile);
1337                     }
1338                 }
1339                 syncDeCeAccountsLocked(accounts);
1340             }
1341             if (validateAccounts) {
1342                 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
1343             }
1344             return accounts;
1345         }
1346     }
1347 
syncDeCeAccountsLocked(UserAccounts accounts)1348     private void syncDeCeAccountsLocked(UserAccounts accounts) {
1349         Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
1350         List<Account> accountsToRemove = accounts.accountsDb.findCeAccountsNotInDe();
1351         if (!accountsToRemove.isEmpty()) {
1352             Slog.i(TAG, accountsToRemove.size()
1353                     + " accounts were previously deleted while user "
1354                     + accounts.userId + " was locked. Removing accounts from CE tables");
1355             logRecord(accounts, AccountsDb.DEBUG_ACTION_SYNC_DE_CE_ACCOUNTS,
1356                     AccountsDb.TABLE_ACCOUNTS);
1357 
1358             for (Account account : accountsToRemove) {
1359                 removeAccountInternal(accounts, account, Process.SYSTEM_UID);
1360             }
1361         }
1362     }
1363 
purgeOldGrantsAll()1364     private void purgeOldGrantsAll() {
1365         synchronized (mUsers) {
1366             for (int i = 0; i < mUsers.size(); i++) {
1367                 purgeOldGrants(mUsers.valueAt(i));
1368             }
1369         }
1370     }
1371 
purgeOldGrants(UserAccounts accounts)1372     private void purgeOldGrants(UserAccounts accounts) {
1373         synchronized (accounts.dbLock) {
1374             synchronized (accounts.cacheLock) {
1375                 List<Integer> uids = accounts.accountsDb.findAllUidGrants();
1376                 for (int uid : uids) {
1377                     final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
1378                     if (packageExists) {
1379                         continue;
1380                     }
1381                     Log.d(TAG, "deleting grants for UID " + uid
1382                             + " because its package is no longer installed");
1383                     accounts.accountsDb.deleteGrantsByUid(uid);
1384                 }
1385             }
1386         }
1387     }
1388 
removeVisibilityValuesForPackage(String packageName)1389     private void removeVisibilityValuesForPackage(String packageName) {
1390         if (isSpecialPackageKey(packageName)) {
1391             return;
1392         }
1393         synchronized (mUsers) {
1394             int numberOfUsers = mUsers.size();
1395             for (int i = 0; i < numberOfUsers; i++) {
1396                 UserAccounts accounts = mUsers.valueAt(i);
1397                 try {
1398                     mPackageManager.getPackageUidAsUser(packageName, accounts.userId);
1399                 } catch (NameNotFoundException e) {
1400                     // package does not exist - remove visibility values
1401                     accounts.accountsDb.deleteAccountVisibilityForPackage(packageName);
1402                     synchronized (accounts.dbLock) {
1403                         synchronized (accounts.cacheLock) {
1404                             for (Account account : accounts.visibilityCache.keySet()) {
1405                                 Map<String, Integer> accountVisibility =
1406                                         getPackagesAndVisibilityForAccountLocked(account, accounts);
1407                                 accountVisibility.remove(packageName);
1408                             }
1409                         }
1410                     }
1411               }
1412           }
1413         }
1414     }
1415 
purgeUserData(int userId)1416     private void purgeUserData(int userId) {
1417         UserAccounts accounts;
1418         synchronized (mUsers) {
1419             accounts = mUsers.get(userId);
1420             mUsers.remove(userId);
1421             mLocalUnlockedUsers.delete(userId);
1422         }
1423         if (accounts != null) {
1424             synchronized (accounts.dbLock) {
1425                 synchronized (accounts.cacheLock) {
1426                     accounts.accountsDb.closeDebugStatement();
1427                     accounts.accountsDb.close();
1428                 }
1429             }
1430         }
1431     }
1432 
1433     @VisibleForTesting
onUserUnlocked(Intent intent)1434     void onUserUnlocked(Intent intent) {
1435         onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
1436     }
1437 
onUnlockUser(int userId)1438     void onUnlockUser(int userId) {
1439         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1440             Log.v(TAG, "onUserUnlocked " + userId);
1441         }
1442         synchronized (mUsers) {
1443             mLocalUnlockedUsers.put(userId, true);
1444         }
1445         if (userId < 1) return;
1446         mHandler.post(() -> syncSharedAccounts(userId));
1447     }
1448 
syncSharedAccounts(int userId)1449     private void syncSharedAccounts(int userId) {
1450         // Check if there's a shared account that needs to be created as an account
1451         Account[] sharedAccounts = getSharedAccountsAsUser(userId);
1452         if (sharedAccounts == null || sharedAccounts.length == 0) return;
1453         Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
1454         int parentUserId = UserManager.isSplitSystemUser()
1455                 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
1456                 : UserHandle.USER_SYSTEM;
1457         if (parentUserId < 0) {
1458             Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
1459             return;
1460         }
1461         for (Account sa : sharedAccounts) {
1462             if (ArrayUtils.contains(accounts, sa)) continue;
1463             // Account doesn't exist. Copy it now.
1464             copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
1465         }
1466     }
1467 
1468     @Override
onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed)1469     public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
1470         validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
1471     }
1472 
1473     @Override
getPassword(Account account)1474     public String getPassword(Account account) {
1475         int callingUid = Binder.getCallingUid();
1476         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1477             Log.v(TAG, "getPassword: " + account
1478                     + ", caller's uid " + Binder.getCallingUid()
1479                     + ", pid " + Binder.getCallingPid());
1480         }
1481         if (account == null) throw new IllegalArgumentException("account is null");
1482         int userId = UserHandle.getCallingUserId();
1483         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1484             String msg = String.format(
1485                     "uid %s cannot get secrets for accounts of type: %s",
1486                     callingUid,
1487                     account.type);
1488             throw new SecurityException(msg);
1489         }
1490         long identityToken = clearCallingIdentity();
1491         try {
1492             UserAccounts accounts = getUserAccounts(userId);
1493             return readPasswordInternal(accounts, account);
1494         } finally {
1495             restoreCallingIdentity(identityToken);
1496         }
1497     }
1498 
readPasswordInternal(UserAccounts accounts, Account account)1499     private String readPasswordInternal(UserAccounts accounts, Account account) {
1500         if (account == null) {
1501             return null;
1502         }
1503         if (!isLocalUnlockedUser(accounts.userId)) {
1504             Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
1505             return null;
1506         }
1507 
1508         synchronized (accounts.dbLock) {
1509             synchronized (accounts.cacheLock) {
1510                 return accounts.accountsDb
1511                         .findAccountPasswordByNameAndType(account.name, account.type);
1512             }
1513         }
1514     }
1515 
1516     @Override
getPreviousName(Account account)1517     public String getPreviousName(Account account) {
1518         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1519             Log.v(TAG, "getPreviousName: " + account
1520                     + ", caller's uid " + Binder.getCallingUid()
1521                     + ", pid " + Binder.getCallingPid());
1522         }
1523         Preconditions.checkNotNull(account, "account cannot be null");
1524         int userId = UserHandle.getCallingUserId();
1525         long identityToken = clearCallingIdentity();
1526         try {
1527             UserAccounts accounts = getUserAccounts(userId);
1528             return readPreviousNameInternal(accounts, account);
1529         } finally {
1530             restoreCallingIdentity(identityToken);
1531         }
1532     }
1533 
readPreviousNameInternal(UserAccounts accounts, Account account)1534     private String readPreviousNameInternal(UserAccounts accounts, Account account) {
1535         if  (account == null) {
1536             return null;
1537         }
1538         synchronized (accounts.dbLock) {
1539             synchronized (accounts.cacheLock) {
1540                 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
1541                 if (previousNameRef == null) {
1542                     String previousName = accounts.accountsDb.findDeAccountPreviousName(account);
1543                     previousNameRef = new AtomicReference<>(previousName);
1544                     accounts.previousNameCache.put(account, previousNameRef);
1545                     return previousName;
1546                 } else {
1547                     return previousNameRef.get();
1548                 }
1549             }
1550         }
1551     }
1552 
1553     @Override
getUserData(Account account, String key)1554     public String getUserData(Account account, String key) {
1555         final int callingUid = Binder.getCallingUid();
1556         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1557             String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
1558                     account, key, callingUid, Binder.getCallingPid());
1559             Log.v(TAG, msg);
1560         }
1561         Preconditions.checkNotNull(account, "account cannot be null");
1562         Preconditions.checkNotNull(key, "key cannot be null");
1563         int userId = UserHandle.getCallingUserId();
1564         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1565             String msg = String.format(
1566                     "uid %s cannot get user data for accounts of type: %s",
1567                     callingUid,
1568                     account.type);
1569             throw new SecurityException(msg);
1570         }
1571         if (!isLocalUnlockedUser(userId)) {
1572             Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
1573             return null;
1574         }
1575         long identityToken = clearCallingIdentity();
1576         try {
1577             UserAccounts accounts = getUserAccounts(userId);
1578             if (!accountExistsCache(accounts, account)) {
1579                 return null;
1580             }
1581             return readUserDataInternal(accounts, account, key);
1582         } finally {
1583             restoreCallingIdentity(identityToken);
1584         }
1585     }
1586 
1587     @Override
getAuthenticatorTypes(int userId)1588     public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
1589         int callingUid = Binder.getCallingUid();
1590         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1591             Log.v(TAG, "getAuthenticatorTypes: "
1592                     + "for user id " + userId
1593                     + " caller's uid " + callingUid
1594                     + ", pid " + Binder.getCallingPid());
1595         }
1596         // Only allow the system process to read accounts of other users
1597         if (isCrossUser(callingUid, userId)) {
1598             throw new SecurityException(
1599                     String.format(
1600                             "User %s tying to get authenticator types for %s" ,
1601                             UserHandle.getCallingUserId(),
1602                             userId));
1603         }
1604 
1605         final long identityToken = clearCallingIdentity();
1606         try {
1607             return getAuthenticatorTypesInternal(userId);
1608 
1609         } finally {
1610             restoreCallingIdentity(identityToken);
1611         }
1612     }
1613 
1614     /**
1615      * Should only be called inside of a clearCallingIdentity block.
1616      */
getAuthenticatorTypesInternal(int userId)1617     private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
1618         mAuthenticatorCache.updateServices(userId);
1619         Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
1620                 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
1621         AuthenticatorDescription[] types =
1622                 new AuthenticatorDescription[authenticatorCollection.size()];
1623         int i = 0;
1624         for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
1625                 : authenticatorCollection) {
1626             types[i] = authenticator.type;
1627             i++;
1628         }
1629         return types;
1630     }
1631 
isCrossUser(int callingUid, int userId)1632     private boolean isCrossUser(int callingUid, int userId) {
1633         return (userId != UserHandle.getCallingUserId()
1634                 && callingUid != Process.SYSTEM_UID
1635                 && mContext.checkCallingOrSelfPermission(
1636                         android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1637                                 != PackageManager.PERMISSION_GRANTED);
1638     }
1639 
1640     @Override
addAccountExplicitly(Account account, String password, Bundle extras)1641     public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
1642         return addAccountExplicitlyWithVisibility(account, password, extras, null);
1643     }
1644 
1645     @Override
copyAccountToUser(final IAccountManagerResponse response, final Account account, final int userFrom, int userTo)1646     public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
1647             final int userFrom, int userTo) {
1648         int callingUid = Binder.getCallingUid();
1649         if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1650             throw new SecurityException("Calling copyAccountToUser requires "
1651                     + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
1652         }
1653         final UserAccounts fromAccounts = getUserAccounts(userFrom);
1654         final UserAccounts toAccounts = getUserAccounts(userTo);
1655         if (fromAccounts == null || toAccounts == null) {
1656             if (response != null) {
1657                 Bundle result = new Bundle();
1658                 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1659                 try {
1660                     response.onResult(result);
1661                 } catch (RemoteException e) {
1662                     Slog.w(TAG, "Failed to report error back to the client." + e);
1663                 }
1664             }
1665             return;
1666         }
1667 
1668         Slog.d(TAG, "Copying account " + account.toSafeString()
1669                 + " from user " + userFrom + " to user " + userTo);
1670         long identityToken = clearCallingIdentity();
1671         try {
1672             new Session(fromAccounts, response, account.type, false,
1673                     false /* stripAuthTokenFromResult */, account.name,
1674                     false /* authDetailsRequired */) {
1675                 @Override
1676                 protected String toDebugString(long now) {
1677                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
1678                             + ", " + account.type;
1679                 }
1680 
1681                 @Override
1682                 public void run() throws RemoteException {
1683                     mAuthenticator.getAccountCredentialsForCloning(this, account);
1684                 }
1685 
1686                 @Override
1687                 public void onResult(Bundle result) {
1688                     Bundle.setDefusable(result, true);
1689                     if (result != null
1690                             && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1691                         // Create a Session for the target user and pass in the bundle
1692                         completeCloningAccount(response, result, account, toAccounts, userFrom);
1693                     } else {
1694                         super.onResult(result);
1695                     }
1696                 }
1697             }.bind();
1698         } finally {
1699             restoreCallingIdentity(identityToken);
1700         }
1701     }
1702 
1703     @Override
accountAuthenticated(final Account account)1704     public boolean accountAuthenticated(final Account account) {
1705         final int callingUid = Binder.getCallingUid();
1706         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1707             String msg = String.format(
1708                     "accountAuthenticated( account: %s, callerUid: %s)",
1709                     account,
1710                     callingUid);
1711             Log.v(TAG, msg);
1712         }
1713         Preconditions.checkNotNull(account, "account cannot be null");
1714         int userId = UserHandle.getCallingUserId();
1715         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
1716             String msg = String.format(
1717                     "uid %s cannot notify authentication for accounts of type: %s",
1718                     callingUid,
1719                     account.type);
1720             throw new SecurityException(msg);
1721         }
1722 
1723         if (!canUserModifyAccounts(userId, callingUid) ||
1724                 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
1725             return false;
1726         }
1727 
1728         long identityToken = clearCallingIdentity();
1729         try {
1730             UserAccounts accounts = getUserAccounts(userId);
1731             return updateLastAuthenticatedTime(account);
1732         } finally {
1733             restoreCallingIdentity(identityToken);
1734         }
1735     }
1736 
updateLastAuthenticatedTime(Account account)1737     private boolean updateLastAuthenticatedTime(Account account) {
1738         final UserAccounts accounts = getUserAccountsForCaller();
1739         synchronized (accounts.dbLock) {
1740             synchronized (accounts.cacheLock) {
1741                 return accounts.accountsDb.updateAccountLastAuthenticatedTime(account);
1742             }
1743         }
1744     }
1745 
completeCloningAccount(IAccountManagerResponse response, final Bundle accountCredentials, final Account account, final UserAccounts targetUser, final int parentUserId)1746     private void completeCloningAccount(IAccountManagerResponse response,
1747             final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1748             final int parentUserId){
1749         Bundle.setDefusable(accountCredentials, true);
1750         long id = clearCallingIdentity();
1751         try {
1752             new Session(targetUser, response, account.type, false,
1753                     false /* stripAuthTokenFromResult */, account.name,
1754                     false /* authDetailsRequired */) {
1755                 @Override
1756                 protected String toDebugString(long now) {
1757                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
1758                             + ", " + account.type;
1759                 }
1760 
1761                 @Override
1762                 public void run() throws RemoteException {
1763                     // Confirm that the owner's account still exists before this step.
1764                     for (Account acc : getAccounts(parentUserId, mContext.getOpPackageName())) {
1765                         if (acc.equals(account)) {
1766                             mAuthenticator.addAccountFromCredentials(
1767                                     this, account, accountCredentials);
1768                             break;
1769                         }
1770                     }
1771                 }
1772 
1773                 @Override
1774                 public void onResult(Bundle result) {
1775                     Bundle.setDefusable(result, true);
1776                     // TODO: Anything to do if if succedded?
1777                     // TODO: If it failed: Show error notification? Should we remove the shadow
1778                     // account to avoid retries?
1779                     // TODO: what we do with the visibility?
1780 
1781                     super.onResult(result);
1782                 }
1783 
1784                 @Override
1785                 public void onError(int errorCode, String errorMessage) {
1786                     super.onError(errorCode,  errorMessage);
1787                     // TODO: Show error notification to user
1788                     // TODO: Should we remove the shadow account so that it doesn't keep trying?
1789                 }
1790 
1791             }.bind();
1792         } finally {
1793             restoreCallingIdentity(id);
1794         }
1795     }
1796 
addAccountInternal(UserAccounts accounts, Account account, String password, Bundle extras, int callingUid, Map<String, Integer> packageToVisibility)1797     private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
1798             Bundle extras, int callingUid, Map<String, Integer> packageToVisibility) {
1799         Bundle.setDefusable(extras, true);
1800         if (account == null) {
1801             return false;
1802         }
1803         if (!isLocalUnlockedUser(accounts.userId)) {
1804             Log.w(TAG, "Account " + account.toSafeString() + " cannot be added - user "
1805                     + accounts.userId + " is locked. callingUid=" + callingUid);
1806             return false;
1807         }
1808         synchronized (accounts.dbLock) {
1809             synchronized (accounts.cacheLock) {
1810                 accounts.accountsDb.beginTransaction();
1811                 try {
1812                     if (accounts.accountsDb.findCeAccountId(account) >= 0) {
1813                         Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
1814                                 + ", skipping since the account already exists");
1815                         return false;
1816                     }
1817                     long accountId = accounts.accountsDb.insertCeAccount(account, password);
1818                     if (accountId < 0) {
1819                         Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
1820                                 + ", skipping the DB insert failed");
1821                         return false;
1822                     }
1823                     // Insert into DE table
1824                     if (accounts.accountsDb.insertDeAccount(account, accountId) < 0) {
1825                         Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
1826                                 + ", skipping the DB insert failed");
1827                         return false;
1828                     }
1829                     if (extras != null) {
1830                         for (String key : extras.keySet()) {
1831                             final String value = extras.getString(key);
1832                             if (accounts.accountsDb.insertExtra(accountId, key, value) < 0) {
1833                                 Log.w(TAG, "insertAccountIntoDatabase: "
1834                                         + account.toSafeString()
1835                                         + ", skipping since insertExtra failed for key " + key);
1836                                 return false;
1837                             }
1838                         }
1839                     }
1840 
1841                     if (packageToVisibility != null) {
1842                         for (Entry<String, Integer> entry : packageToVisibility.entrySet()) {
1843                             setAccountVisibility(account, entry.getKey() /* package */,
1844                                     entry.getValue() /* visibility */, false /* notify */,
1845                                     accounts);
1846                         }
1847                     }
1848                     accounts.accountsDb.setTransactionSuccessful();
1849 
1850                     logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
1851                             accountId,
1852                             accounts, callingUid);
1853 
1854                     insertAccountIntoCacheLocked(accounts, account);
1855                 } finally {
1856                     accounts.accountsDb.endTransaction();
1857                 }
1858             }
1859         }
1860         if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1861             addAccountToLinkedRestrictedUsers(account, accounts.userId);
1862         }
1863 
1864         sendNotificationAccountUpdated(account, accounts);
1865         // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1866         sendAccountsChangedBroadcast(accounts.userId);
1867 
1868         return true;
1869     }
1870 
isLocalUnlockedUser(int userId)1871     private boolean isLocalUnlockedUser(int userId) {
1872         synchronized (mUsers) {
1873             return mLocalUnlockedUsers.get(userId);
1874         }
1875     }
1876 
1877     /**
1878      * Adds the account to all linked restricted users as shared accounts. If the user is currently
1879      * running, then clone the account too.
1880      * @param account the account to share with limited users
1881      *
1882      */
addAccountToLinkedRestrictedUsers(Account account, int parentUserId)1883     private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
1884         List<UserInfo> users = getUserManager().getUsers();
1885         for (UserInfo user : users) {
1886             if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
1887                 addSharedAccountAsUser(account, user.id);
1888                 if (isLocalUnlockedUser(user.id)) {
1889                     mHandler.sendMessage(mHandler.obtainMessage(
1890                             MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
1891                 }
1892             }
1893         }
1894     }
1895 
1896     @Override
hasFeatures(IAccountManagerResponse response, Account account, String[] features, String opPackageName)1897     public void hasFeatures(IAccountManagerResponse response,
1898             Account account, String[] features, String opPackageName) {
1899         int callingUid = Binder.getCallingUid();
1900         mAppOpsManager.checkPackage(callingUid, opPackageName);
1901         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1902             Log.v(TAG, "hasFeatures: " + account
1903                     + ", response " + response
1904                     + ", features " + Arrays.toString(features)
1905                     + ", caller's uid " + callingUid
1906                     + ", pid " + Binder.getCallingPid());
1907         }
1908         Preconditions.checkArgument(account != null, "account cannot be null");
1909         Preconditions.checkArgument(response != null, "response cannot be null");
1910         Preconditions.checkArgument(features != null, "features cannot be null");
1911         int userId = UserHandle.getCallingUserId();
1912         checkReadAccountsPermitted(callingUid, account.type, userId,
1913                 opPackageName);
1914 
1915         long identityToken = clearCallingIdentity();
1916         try {
1917             UserAccounts accounts = getUserAccounts(userId);
1918             new TestFeaturesSession(accounts, response, account, features).bind();
1919         } finally {
1920             restoreCallingIdentity(identityToken);
1921         }
1922     }
1923 
1924     private class TestFeaturesSession extends Session {
1925         private final String[] mFeatures;
1926         private final Account mAccount;
1927 
TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response, Account account, String[] features)1928         public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
1929                 Account account, String[] features) {
1930             super(accounts, response, account.type, false /* expectActivityLaunch */,
1931                     true /* stripAuthTokenFromResult */, account.name,
1932                     false /* authDetailsRequired */);
1933             mFeatures = features;
1934             mAccount = account;
1935         }
1936 
1937         @Override
run()1938         public void run() throws RemoteException {
1939             try {
1940                 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1941             } catch (RemoteException e) {
1942                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1943             }
1944         }
1945 
1946         @Override
onResult(Bundle result)1947         public void onResult(Bundle result) {
1948             Bundle.setDefusable(result, true);
1949             IAccountManagerResponse response = getResponseAndClose();
1950             if (response != null) {
1951                 try {
1952                     if (result == null) {
1953                         response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
1954                         return;
1955                     }
1956                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
1957                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1958                                 + response);
1959                     }
1960                     final Bundle newResult = new Bundle();
1961                     newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1962                             result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1963                     response.onResult(newResult);
1964                 } catch (RemoteException e) {
1965                     // if the caller is dead then there is no one to care about remote exceptions
1966                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
1967                         Log.v(TAG, "failure while notifying response", e);
1968                     }
1969                 }
1970             }
1971         }
1972 
1973         @Override
toDebugString(long now)1974         protected String toDebugString(long now) {
1975             return super.toDebugString(now) + ", hasFeatures"
1976                     + ", " + mAccount
1977                     + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1978         }
1979     }
1980 
1981     @Override
renameAccount( IAccountManagerResponse response, Account accountToRename, String newName)1982     public void renameAccount(
1983             IAccountManagerResponse response, Account accountToRename, String newName) {
1984         final int callingUid = Binder.getCallingUid();
1985         if (Log.isLoggable(TAG, Log.VERBOSE)) {
1986             Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
1987                 + ", caller's uid " + callingUid
1988                 + ", pid " + Binder.getCallingPid());
1989         }
1990         if (accountToRename == null) throw new IllegalArgumentException("account is null");
1991         int userId = UserHandle.getCallingUserId();
1992         if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
1993             String msg = String.format(
1994                     "uid %s cannot rename accounts of type: %s",
1995                     callingUid,
1996                     accountToRename.type);
1997             throw new SecurityException(msg);
1998         }
1999         long identityToken = clearCallingIdentity();
2000         try {
2001             UserAccounts accounts = getUserAccounts(userId);
2002             Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
2003             Bundle result = new Bundle();
2004             result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
2005             result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
2006             result.putString(AccountManager.KEY_ACCOUNT_ACCESS_ID,
2007                     resultingAccount.getAccessId());
2008             try {
2009                 response.onResult(result);
2010             } catch (RemoteException e) {
2011                 Log.w(TAG, e.getMessage());
2012             }
2013         } finally {
2014             restoreCallingIdentity(identityToken);
2015         }
2016     }
2017 
renameAccountInternal( UserAccounts accounts, Account accountToRename, String newName)2018     private Account renameAccountInternal(
2019             UserAccounts accounts, Account accountToRename, String newName) {
2020         Account resultAccount = null;
2021         /*
2022          * Cancel existing notifications. Let authenticators
2023          * re-post notifications as required. But we don't know if
2024          * the authenticators have bound their notifications to
2025          * now stale account name data.
2026          *
2027          * With a rename api, we might not need to do this anymore but it
2028          * shouldn't hurt.
2029          */
2030         cancelNotification(
2031                 getSigninRequiredNotificationId(accounts, accountToRename),
2032                 new UserHandle(accounts.userId));
2033         synchronized(accounts.credentialsPermissionNotificationIds) {
2034             for (Pair<Pair<Account, String>, Integer> pair:
2035                     accounts.credentialsPermissionNotificationIds.keySet()) {
2036                 if (accountToRename.equals(pair.first.first)) {
2037                     NotificationId id = accounts.credentialsPermissionNotificationIds.get(pair);
2038                     cancelNotification(id, new UserHandle(accounts.userId));
2039                 }
2040             }
2041         }
2042         synchronized (accounts.dbLock) {
2043             synchronized (accounts.cacheLock) {
2044                 List<String> accountRemovedReceivers =
2045                     getAccountRemovedReceivers(accountToRename, accounts);
2046                 accounts.accountsDb.beginTransaction();
2047                 Account renamedAccount = new Account(newName, accountToRename.type);
2048                 try {
2049                     if ((accounts.accountsDb.findCeAccountId(renamedAccount) >= 0)) {
2050                         Log.e(TAG, "renameAccount failed - account with new name already exists");
2051                         return null;
2052                     }
2053                     final long accountId = accounts.accountsDb.findDeAccountId(accountToRename);
2054                     if (accountId >= 0) {
2055                         accounts.accountsDb.renameCeAccount(accountId, newName);
2056                         if (accounts.accountsDb.renameDeAccount(
2057                                 accountId, newName, accountToRename.name)) {
2058                             accounts.accountsDb.setTransactionSuccessful();
2059                         } else {
2060                             Log.e(TAG, "renameAccount failed");
2061                             return null;
2062                         }
2063                     } else {
2064                         Log.e(TAG, "renameAccount failed - old account does not exist");
2065                         return null;
2066                     }
2067                 } finally {
2068                     accounts.accountsDb.endTransaction();
2069                 }
2070             /*
2071              * Database transaction was successful. Clean up cached
2072              * data associated with the account in the user profile.
2073              */
2074                 renamedAccount = insertAccountIntoCacheLocked(accounts, renamedAccount);
2075             /*
2076              * Extract the data and token caches before removing the
2077              * old account to preserve the user data associated with
2078              * the account.
2079              */
2080                 Map<String, String> tmpData = accounts.userDataCache.get(accountToRename);
2081                 Map<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
2082                 Map<String, Integer> tmpVisibility = accounts.visibilityCache.get(accountToRename);
2083                 removeAccountFromCacheLocked(accounts, accountToRename);
2084             /*
2085              * Update the cached data associated with the renamed
2086              * account.
2087              */
2088                 accounts.userDataCache.put(renamedAccount, tmpData);
2089                 accounts.authTokenCache.put(renamedAccount, tmpTokens);
2090                 accounts.visibilityCache.put(renamedAccount, tmpVisibility);
2091                 accounts.previousNameCache.put(
2092                         renamedAccount,
2093                         new AtomicReference<>(accountToRename.name));
2094                 resultAccount = renamedAccount;
2095 
2096                 int parentUserId = accounts.userId;
2097                 if (canHaveProfile(parentUserId)) {
2098                 /*
2099                  * Owner or system user account was renamed, rename the account for
2100                  * those users with which the account was shared.
2101                  */
2102                     List<UserInfo> users = getUserManager().getUsers(true);
2103                     for (UserInfo user : users) {
2104                         if (user.isRestricted()
2105                                 && (user.restrictedProfileParentId == parentUserId)) {
2106                             renameSharedAccountAsUser(accountToRename, newName, user.id);
2107                         }
2108                     }
2109                 }
2110 
2111                 sendNotificationAccountUpdated(resultAccount, accounts);
2112                 sendAccountsChangedBroadcast(accounts.userId);
2113                 for (String packageName : accountRemovedReceivers) {
2114                     sendAccountRemovedBroadcast(accountToRename, packageName, accounts.userId);
2115                 }
2116             }
2117         }
2118         return resultAccount;
2119     }
2120 
canHaveProfile(final int parentUserId)2121     private boolean canHaveProfile(final int parentUserId) {
2122         final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
2123         return userInfo != null && userInfo.canHaveProfile();
2124     }
2125 
2126     @Override
removeAccount(IAccountManagerResponse response, Account account, boolean expectActivityLaunch)2127     public void removeAccount(IAccountManagerResponse response, Account account,
2128             boolean expectActivityLaunch) {
2129         removeAccountAsUser(
2130                 response,
2131                 account,
2132                 expectActivityLaunch,
2133                 UserHandle.getCallingUserId());
2134     }
2135 
2136     @Override
removeAccountAsUser(IAccountManagerResponse response, Account account, boolean expectActivityLaunch, int userId)2137     public void removeAccountAsUser(IAccountManagerResponse response, Account account,
2138             boolean expectActivityLaunch, int userId) {
2139         final int callingUid = Binder.getCallingUid();
2140         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2141             Log.v(TAG, "removeAccount: " + account
2142                     + ", response " + response
2143                     + ", caller's uid " + callingUid
2144                     + ", pid " + Binder.getCallingPid()
2145                     + ", for user id " + userId);
2146         }
2147         Preconditions.checkArgument(account != null, "account cannot be null");
2148         Preconditions.checkArgument(response != null, "response cannot be null");
2149 
2150         // Only allow the system process to modify accounts of other users
2151         if (isCrossUser(callingUid, userId)) {
2152             throw new SecurityException(
2153                     String.format(
2154                             "User %s tying remove account for %s" ,
2155                             UserHandle.getCallingUserId(),
2156                             userId));
2157         }
2158         /*
2159          * Only the system, authenticator or profile owner should be allowed to remove accounts for
2160          * that authenticator.  This will let users remove accounts (via Settings in the system) but
2161          * not arbitrary applications (like competing authenticators).
2162          */
2163         UserHandle user = UserHandle.of(userId);
2164         if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
2165                 && !isSystemUid(callingUid)
2166                 && !isProfileOwner(callingUid)) {
2167             String msg = String.format(
2168                     "uid %s cannot remove accounts of type: %s",
2169                     callingUid,
2170                     account.type);
2171             throw new SecurityException(msg);
2172         }
2173         if (!canUserModifyAccounts(userId, callingUid)) {
2174             try {
2175                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2176                         "User cannot modify accounts");
2177             } catch (RemoteException re) {
2178             }
2179             return;
2180         }
2181         if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
2182             try {
2183                 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2184                         "User cannot modify accounts of this type (policy).");
2185             } catch (RemoteException re) {
2186             }
2187             return;
2188         }
2189         long identityToken = clearCallingIdentity();
2190         UserAccounts accounts = getUserAccounts(userId);
2191         cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
2192         synchronized(accounts.credentialsPermissionNotificationIds) {
2193             for (Pair<Pair<Account, String>, Integer> pair:
2194                 accounts.credentialsPermissionNotificationIds.keySet()) {
2195                 if (account.equals(pair.first.first)) {
2196                     NotificationId id = accounts.credentialsPermissionNotificationIds.get(pair);
2197                     cancelNotification(id, user);
2198                 }
2199             }
2200         }
2201         final long accountId = accounts.accountsDb.findDeAccountId(account);
2202         logRecord(
2203                 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
2204                 AccountsDb.TABLE_ACCOUNTS,
2205                 accountId,
2206                 accounts,
2207                 callingUid);
2208         try {
2209             new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
2210         } finally {
2211             restoreCallingIdentity(identityToken);
2212         }
2213     }
2214 
2215     @Override
removeAccountExplicitly(Account account)2216     public boolean removeAccountExplicitly(Account account) {
2217         final int callingUid = Binder.getCallingUid();
2218         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2219             Log.v(TAG, "removeAccountExplicitly: " + account
2220                     + ", caller's uid " + callingUid
2221                     + ", pid " + Binder.getCallingPid());
2222         }
2223         int userId = Binder.getCallingUserHandle().getIdentifier();
2224         if (account == null) {
2225             /*
2226              * Null accounts should result in returning false, as per
2227              * AccountManage.addAccountExplicitly(...) java doc.
2228              */
2229             Log.e(TAG, "account is null");
2230             return false;
2231         } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2232             String msg = String.format(
2233                     "uid %s cannot explicitly remove accounts of type: %s",
2234                     callingUid,
2235                     account.type);
2236             throw new SecurityException(msg);
2237         }
2238         UserAccounts accounts = getUserAccountsForCaller();
2239         final long accountId = accounts.accountsDb.findDeAccountId(account);
2240         logRecord(
2241                 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
2242                 AccountsDb.TABLE_ACCOUNTS,
2243                 accountId,
2244                 accounts,
2245                 callingUid);
2246         long identityToken = clearCallingIdentity();
2247         try {
2248             return removeAccountInternal(accounts, account, callingUid);
2249         } finally {
2250             restoreCallingIdentity(identityToken);
2251         }
2252     }
2253 
2254     private class RemoveAccountSession extends Session {
2255         final Account mAccount;
RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response, Account account, boolean expectActivityLaunch)2256         public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
2257                 Account account, boolean expectActivityLaunch) {
2258             super(accounts, response, account.type, expectActivityLaunch,
2259                     true /* stripAuthTokenFromResult */, account.name,
2260                     false /* authDetailsRequired */);
2261             mAccount = account;
2262         }
2263 
2264         @Override
toDebugString(long now)2265         protected String toDebugString(long now) {
2266             return super.toDebugString(now) + ", removeAccount"
2267                     + ", account " + mAccount;
2268         }
2269 
2270         @Override
run()2271         public void run() throws RemoteException {
2272             mAuthenticator.getAccountRemovalAllowed(this, mAccount);
2273         }
2274 
2275         @Override
onResult(Bundle result)2276         public void onResult(Bundle result) {
2277             Bundle.setDefusable(result, true);
2278             if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
2279                     && !result.containsKey(AccountManager.KEY_INTENT)) {
2280                 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
2281                 if (removalAllowed) {
2282                     removeAccountInternal(mAccounts, mAccount, getCallingUid());
2283                 }
2284                 IAccountManagerResponse response = getResponseAndClose();
2285                 if (response != null) {
2286                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
2287                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2288                                 + response);
2289                     }
2290                     try {
2291                         response.onResult(result);
2292                     } catch (RemoteException e) {
2293                         Slog.e(TAG, "Error calling onResult()", e);
2294                     }
2295                 }
2296             }
2297             super.onResult(result);
2298         }
2299     }
2300 
2301     @VisibleForTesting
removeAccountInternal(Account account)2302     protected void removeAccountInternal(Account account) {
2303         removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
2304     }
2305 
removeAccountInternal(UserAccounts accounts, Account account, int callingUid)2306     private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
2307         boolean isChanged = false;
2308         boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
2309         if (!userUnlocked) {
2310             Slog.i(TAG, "Removing account " + account.toSafeString()
2311                     + " while user " + accounts.userId
2312                     + " is still locked. CE data will be removed later");
2313         }
2314         synchronized (accounts.dbLock) {
2315             synchronized (accounts.cacheLock) {
2316                 Map<String, Integer> packagesToVisibility = getRequestingPackages(account,
2317                         accounts);
2318                 List<String> accountRemovedReceivers =
2319                     getAccountRemovedReceivers(account, accounts);
2320                 accounts.accountsDb.beginTransaction();
2321                 // Set to a dummy value, this will only be used if the database
2322                 // transaction succeeds.
2323                 long accountId = -1;
2324                 try {
2325                     accountId = accounts.accountsDb.findDeAccountId(account);
2326                     if (accountId >= 0) {
2327                         isChanged = accounts.accountsDb.deleteDeAccount(accountId);
2328                     }
2329                     // always delete from CE table if CE storage is available
2330                     // DE account could be removed while CE was locked
2331                     if (userUnlocked) {
2332                         long ceAccountId = accounts.accountsDb.findCeAccountId(account);
2333                         if (ceAccountId >= 0) {
2334                             accounts.accountsDb.deleteCeAccount(ceAccountId);
2335                         }
2336                     }
2337                     accounts.accountsDb.setTransactionSuccessful();
2338                 } finally {
2339                     accounts.accountsDb.endTransaction();
2340                 }
2341                 if (isChanged) {
2342                     removeAccountFromCacheLocked(accounts, account);
2343                     for (Entry<String, Integer> packageToVisibility : packagesToVisibility
2344                             .entrySet()) {
2345                         if ((packageToVisibility.getValue() == AccountManager.VISIBILITY_VISIBLE)
2346                                 || (packageToVisibility.getValue()
2347                                     == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE)) {
2348                             notifyPackage(packageToVisibility.getKey(), accounts);
2349                         }
2350                     }
2351 
2352                     // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occurred.
2353                     sendAccountsChangedBroadcast(accounts.userId);
2354                     for (String packageName : accountRemovedReceivers) {
2355                         sendAccountRemovedBroadcast(account, packageName, accounts.userId);
2356                     }
2357                     String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
2358                             : AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
2359                     logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
2360                 }
2361             }
2362         }
2363         long id = Binder.clearCallingIdentity();
2364         try {
2365             int parentUserId = accounts.userId;
2366             if (canHaveProfile(parentUserId)) {
2367                 // Remove from any restricted profiles that are sharing this account.
2368                 List<UserInfo> users = getUserManager().getUsers(true);
2369                 for (UserInfo user : users) {
2370                     if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
2371                         removeSharedAccountAsUser(account, user.id, callingUid);
2372                     }
2373                 }
2374             }
2375         } finally {
2376             Binder.restoreCallingIdentity(id);
2377         }
2378 
2379         if (isChanged) {
2380             synchronized (accounts.credentialsPermissionNotificationIds) {
2381                 for (Pair<Pair<Account, String>, Integer> key
2382                         : accounts.credentialsPermissionNotificationIds.keySet()) {
2383                     if (account.equals(key.first.first)
2384                             && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) {
2385                         final int uid = (Integer) key.second;
2386                         mHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
2387                                 account, uid, false));
2388                     }
2389                 }
2390             }
2391         }
2392 
2393         return isChanged;
2394     }
2395 
2396     @Override
invalidateAuthToken(String accountType, String authToken)2397     public void invalidateAuthToken(String accountType, String authToken) {
2398         int callerUid = Binder.getCallingUid();
2399         Preconditions.checkNotNull(accountType, "accountType cannot be null");
2400         Preconditions.checkNotNull(authToken, "authToken cannot be null");
2401         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2402             Log.v(TAG, "invalidateAuthToken: accountType " + accountType
2403                     + ", caller's uid " + callerUid
2404                     + ", pid " + Binder.getCallingPid());
2405         }
2406         int userId = UserHandle.getCallingUserId();
2407         long identityToken = clearCallingIdentity();
2408         try {
2409             UserAccounts accounts = getUserAccounts(userId);
2410             List<Pair<Account, String>> deletedTokens;
2411             synchronized (accounts.dbLock) {
2412                 accounts.accountsDb.beginTransaction();
2413                 try {
2414                     deletedTokens = invalidateAuthTokenLocked(accounts, accountType, authToken);
2415                     accounts.accountsDb.setTransactionSuccessful();
2416                 } finally {
2417                     accounts.accountsDb.endTransaction();
2418                 }
2419                 synchronized (accounts.cacheLock) {
2420                     for (Pair<Account, String> tokenInfo : deletedTokens) {
2421                         Account act = tokenInfo.first;
2422                         String tokenType = tokenInfo.second;
2423                         writeAuthTokenIntoCacheLocked(accounts, act, tokenType, null);
2424                     }
2425                     // wipe out cached token in memory.
2426                     accounts.accountTokenCaches.remove(accountType, authToken);
2427                 }
2428             }
2429         } finally {
2430             restoreCallingIdentity(identityToken);
2431         }
2432     }
2433 
invalidateAuthTokenLocked(UserAccounts accounts, String accountType, String authToken)2434     private List<Pair<Account, String>> invalidateAuthTokenLocked(UserAccounts accounts, String accountType,
2435             String authToken) {
2436         // TODO Move to AccountsDB
2437         List<Pair<Account, String>> results = new ArrayList<>();
2438         Cursor cursor = accounts.accountsDb.findAuthtokenForAllAccounts(accountType, authToken);
2439 
2440         try {
2441             while (cursor.moveToNext()) {
2442                 String authTokenId = cursor.getString(0);
2443                 String accountName = cursor.getString(1);
2444                 String authTokenType = cursor.getString(2);
2445                 accounts.accountsDb.deleteAuthToken(authTokenId);
2446                 results.add(Pair.create(new Account(accountName, accountType), authTokenType));
2447             }
2448         } finally {
2449             cursor.close();
2450         }
2451         return results;
2452     }
2453 
saveCachedToken( UserAccounts accounts, Account account, String callerPkg, byte[] callerSigDigest, String tokenType, String token, long expiryMillis)2454     private void saveCachedToken(
2455             UserAccounts accounts,
2456             Account account,
2457             String callerPkg,
2458             byte[] callerSigDigest,
2459             String tokenType,
2460             String token,
2461             long expiryMillis) {
2462 
2463         if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
2464             return;
2465         }
2466         cancelNotification(getSigninRequiredNotificationId(accounts, account),
2467                 UserHandle.of(accounts.userId));
2468         synchronized (accounts.cacheLock) {
2469             accounts.accountTokenCaches.put(
2470                     account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
2471         }
2472     }
2473 
saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type, String authToken)2474     private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
2475             String authToken) {
2476         if (account == null || type == null) {
2477             return false;
2478         }
2479         cancelNotification(getSigninRequiredNotificationId(accounts, account),
2480                 UserHandle.of(accounts.userId));
2481         synchronized (accounts.dbLock) {
2482             accounts.accountsDb.beginTransaction();
2483             boolean updateCache = false;
2484             try {
2485                 long accountId = accounts.accountsDb.findDeAccountId(account);
2486                 if (accountId < 0) {
2487                     return false;
2488                 }
2489                 accounts.accountsDb.deleteAuthtokensByAccountIdAndType(accountId, type);
2490                 if (accounts.accountsDb.insertAuthToken(accountId, type, authToken) >= 0) {
2491                     accounts.accountsDb.setTransactionSuccessful();
2492                     updateCache = true;
2493                     return true;
2494                 }
2495                 return false;
2496             } finally {
2497                 accounts.accountsDb.endTransaction();
2498                 if (updateCache) {
2499                     synchronized (accounts.cacheLock) {
2500                         writeAuthTokenIntoCacheLocked(accounts, account, type, authToken);
2501                     }
2502                 }
2503             }
2504         }
2505     }
2506 
2507     @Override
peekAuthToken(Account account, String authTokenType)2508     public String peekAuthToken(Account account, String authTokenType) {
2509         final int callingUid = Binder.getCallingUid();
2510         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2511             Log.v(TAG, "peekAuthToken: " + account
2512                     + ", authTokenType " + authTokenType
2513                     + ", caller's uid " + callingUid
2514                     + ", pid " + Binder.getCallingPid());
2515         }
2516         Preconditions.checkNotNull(account, "account cannot be null");
2517         Preconditions.checkNotNull(authTokenType, "authTokenType cannot be null");
2518         int userId = UserHandle.getCallingUserId();
2519         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2520             String msg = String.format(
2521                     "uid %s cannot peek the authtokens associated with accounts of type: %s",
2522                     callingUid,
2523                     account.type);
2524             throw new SecurityException(msg);
2525         }
2526         if (!isLocalUnlockedUser(userId)) {
2527             Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
2528                     + callingUid);
2529             return null;
2530         }
2531         long identityToken = clearCallingIdentity();
2532         try {
2533             UserAccounts accounts = getUserAccounts(userId);
2534             return readAuthTokenInternal(accounts, account, authTokenType);
2535         } finally {
2536             restoreCallingIdentity(identityToken);
2537         }
2538     }
2539 
2540     @Override
setAuthToken(Account account, String authTokenType, String authToken)2541     public void setAuthToken(Account account, String authTokenType, String authToken) {
2542         final int callingUid = Binder.getCallingUid();
2543         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2544             Log.v(TAG, "setAuthToken: " + account
2545                     + ", authTokenType " + authTokenType
2546                     + ", caller's uid " + callingUid
2547                     + ", pid " + Binder.getCallingPid());
2548         }
2549         Preconditions.checkNotNull(account, "account cannot be null");
2550         Preconditions.checkNotNull(authTokenType, "authTokenType cannot be null");
2551         int userId = UserHandle.getCallingUserId();
2552         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2553             String msg = String.format(
2554                     "uid %s cannot set auth tokens associated with accounts of type: %s",
2555                     callingUid,
2556                     account.type);
2557             throw new SecurityException(msg);
2558         }
2559         long identityToken = clearCallingIdentity();
2560         try {
2561             UserAccounts accounts = getUserAccounts(userId);
2562             saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
2563         } finally {
2564             restoreCallingIdentity(identityToken);
2565         }
2566     }
2567 
2568     @Override
setPassword(Account account, String password)2569     public void setPassword(Account account, String password) {
2570         final int callingUid = Binder.getCallingUid();
2571         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2572             Log.v(TAG, "setAuthToken: " + account
2573                     + ", caller's uid " + callingUid
2574                     + ", pid " + Binder.getCallingPid());
2575         }
2576         Preconditions.checkNotNull(account, "account cannot be null");
2577         int userId = UserHandle.getCallingUserId();
2578         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2579             String msg = String.format(
2580                     "uid %s cannot set secrets for accounts of type: %s",
2581                     callingUid,
2582                     account.type);
2583             throw new SecurityException(msg);
2584         }
2585         long identityToken = clearCallingIdentity();
2586         try {
2587             UserAccounts accounts = getUserAccounts(userId);
2588             setPasswordInternal(accounts, account, password, callingUid);
2589         } finally {
2590             restoreCallingIdentity(identityToken);
2591         }
2592     }
2593 
setPasswordInternal(UserAccounts accounts, Account account, String password, int callingUid)2594     private void setPasswordInternal(UserAccounts accounts, Account account, String password,
2595             int callingUid) {
2596         if (account == null) {
2597             return;
2598         }
2599         boolean isChanged = false;
2600         synchronized (accounts.dbLock) {
2601             synchronized (accounts.cacheLock) {
2602                 accounts.accountsDb.beginTransaction();
2603                 try {
2604                     final long accountId = accounts.accountsDb.findDeAccountId(account);
2605                     if (accountId >= 0) {
2606                         accounts.accountsDb.updateCeAccountPassword(accountId, password);
2607                         accounts.accountsDb.deleteAuthTokensByAccountId(accountId);
2608                         accounts.authTokenCache.remove(account);
2609                         accounts.accountTokenCaches.remove(account);
2610                         accounts.accountsDb.setTransactionSuccessful();
2611                         // If there is an account whose password will be updated and the database
2612                         // transactions succeed, then we say that a change has occured. Even if the
2613                         // new password is the same as the old and there were no authtokens to
2614                         // delete.
2615                         isChanged = true;
2616                         String action = (password == null || password.length() == 0) ?
2617                                 AccountsDb.DEBUG_ACTION_CLEAR_PASSWORD
2618                                 : AccountsDb.DEBUG_ACTION_SET_PASSWORD;
2619                         logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts,
2620                                 callingUid);
2621                     }
2622                 } finally {
2623                     accounts.accountsDb.endTransaction();
2624                     if (isChanged) {
2625                         // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
2626                         sendNotificationAccountUpdated(account, accounts);
2627                         sendAccountsChangedBroadcast(accounts.userId);
2628                     }
2629                 }
2630             }
2631         }
2632     }
2633 
2634     @Override
clearPassword(Account account)2635     public void clearPassword(Account account) {
2636         final int callingUid = Binder.getCallingUid();
2637         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2638             Log.v(TAG, "clearPassword: " + account
2639                     + ", caller's uid " + callingUid
2640                     + ", pid " + Binder.getCallingPid());
2641         }
2642         Preconditions.checkNotNull(account, "account cannot be null");
2643         int userId = UserHandle.getCallingUserId();
2644         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2645             String msg = String.format(
2646                     "uid %s cannot clear passwords for accounts of type: %s",
2647                     callingUid,
2648                     account.type);
2649             throw new SecurityException(msg);
2650         }
2651         long identityToken = clearCallingIdentity();
2652         try {
2653             UserAccounts accounts = getUserAccounts(userId);
2654             setPasswordInternal(accounts, account, null, callingUid);
2655         } finally {
2656             restoreCallingIdentity(identityToken);
2657         }
2658     }
2659 
2660     @Override
setUserData(Account account, String key, String value)2661     public void setUserData(Account account, String key, String value) {
2662         final int callingUid = Binder.getCallingUid();
2663         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2664             Log.v(TAG, "setUserData: " + account
2665                     + ", key " + key
2666                     + ", caller's uid " + callingUid
2667                     + ", pid " + Binder.getCallingPid());
2668         }
2669         if (key == null) throw new IllegalArgumentException("key is null");
2670         if (account == null) throw new IllegalArgumentException("account is null");
2671         int userId = UserHandle.getCallingUserId();
2672         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
2673             String msg = String.format(
2674                     "uid %s cannot set user data for accounts of type: %s",
2675                     callingUid,
2676                     account.type);
2677             throw new SecurityException(msg);
2678         }
2679         long identityToken = clearCallingIdentity();
2680         try {
2681             UserAccounts accounts = getUserAccounts(userId);
2682             if (!accountExistsCache(accounts, account)) {
2683                 return;
2684             }
2685             setUserdataInternal(accounts, account, key, value);
2686         } finally {
2687             restoreCallingIdentity(identityToken);
2688         }
2689     }
2690 
accountExistsCache(UserAccounts accounts, Account account)2691     private boolean accountExistsCache(UserAccounts accounts, Account account) {
2692         synchronized (accounts.cacheLock) {
2693             if (accounts.accountCache.containsKey(account.type)) {
2694                 for (Account acc : accounts.accountCache.get(account.type)) {
2695                     if (acc.name.equals(account.name)) {
2696                         return true;
2697                     }
2698                 }
2699             }
2700         }
2701         return false;
2702     }
2703 
setUserdataInternal(UserAccounts accounts, Account account, String key, String value)2704     private void setUserdataInternal(UserAccounts accounts, Account account, String key,
2705             String value) {
2706         synchronized (accounts.dbLock) {
2707             accounts.accountsDb.beginTransaction();
2708             try {
2709                 long accountId = accounts.accountsDb.findDeAccountId(account);
2710                 if (accountId < 0) {
2711                     return;
2712                 }
2713                 long extrasId = accounts.accountsDb.findExtrasIdByAccountId(accountId, key);
2714                 if (extrasId < 0) {
2715                     extrasId = accounts.accountsDb.insertExtra(accountId, key, value);
2716                     if (extrasId < 0) {
2717                         return;
2718                     }
2719                 } else if (!accounts.accountsDb.updateExtra(extrasId, value)) {
2720                     return;
2721                 }
2722                 accounts.accountsDb.setTransactionSuccessful();
2723             } finally {
2724                 accounts.accountsDb.endTransaction();
2725             }
2726             synchronized (accounts.cacheLock) {
2727                 writeUserDataIntoCacheLocked(accounts, account, key, value);
2728             }
2729         }
2730     }
2731 
onResult(IAccountManagerResponse response, Bundle result)2732     private void onResult(IAccountManagerResponse response, Bundle result) {
2733         if (result == null) {
2734             Log.e(TAG, "the result is unexpectedly null", new Exception());
2735         }
2736         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2737             Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2738                     + response);
2739         }
2740         try {
2741             response.onResult(result);
2742         } catch (RemoteException e) {
2743             // if the caller is dead then there is no one to care about remote
2744             // exceptions
2745             if (Log.isLoggable(TAG, Log.VERBOSE)) {
2746                 Log.v(TAG, "failure while notifying response", e);
2747             }
2748         }
2749     }
2750 
2751     @Override
getAuthTokenLabel(IAccountManagerResponse response, final String accountType, final String authTokenType)2752     public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
2753                                   final String authTokenType)
2754             throws RemoteException {
2755         Preconditions.checkArgument(accountType != null, "accountType cannot be null");
2756         Preconditions.checkArgument(authTokenType != null, "authTokenType cannot be null");
2757 
2758         final int callingUid = getCallingUid();
2759         clearCallingIdentity();
2760         if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
2761             throw new SecurityException("can only call from system");
2762         }
2763         int userId = UserHandle.getUserId(callingUid);
2764         long identityToken = clearCallingIdentity();
2765         try {
2766             UserAccounts accounts = getUserAccounts(userId);
2767             new Session(accounts, response, accountType, false /* expectActivityLaunch */,
2768                     false /* stripAuthTokenFromResult */,  null /* accountName */,
2769                     false /* authDetailsRequired */) {
2770                 @Override
2771                 protected String toDebugString(long now) {
2772                     return super.toDebugString(now) + ", getAuthTokenLabel"
2773                             + ", " + accountType
2774                             + ", authTokenType " + authTokenType;
2775                 }
2776 
2777                 @Override
2778                 public void run() throws RemoteException {
2779                     mAuthenticator.getAuthTokenLabel(this, authTokenType);
2780                 }
2781 
2782                 @Override
2783                 public void onResult(Bundle result) {
2784                     Bundle.setDefusable(result, true);
2785                     if (result != null) {
2786                         String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
2787                         Bundle bundle = new Bundle();
2788                         bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
2789                         super.onResult(bundle);
2790                         return;
2791                     } else {
2792                         super.onResult(result);
2793                     }
2794                 }
2795             }.bind();
2796         } finally {
2797             restoreCallingIdentity(identityToken);
2798         }
2799     }
2800 
2801     @Override
getAuthToken( IAccountManagerResponse response, final Account account, final String authTokenType, final boolean notifyOnAuthFailure, final boolean expectActivityLaunch, final Bundle loginOptions)2802     public void getAuthToken(
2803             IAccountManagerResponse response,
2804             final Account account,
2805             final String authTokenType,
2806             final boolean notifyOnAuthFailure,
2807             final boolean expectActivityLaunch,
2808             final Bundle loginOptions) {
2809         Bundle.setDefusable(loginOptions, true);
2810         if (Log.isLoggable(TAG, Log.VERBOSE)) {
2811             Log.v(TAG, "getAuthToken: " + account
2812                     + ", response " + response
2813                     + ", authTokenType " + authTokenType
2814                     + ", notifyOnAuthFailure " + notifyOnAuthFailure
2815                     + ", expectActivityLaunch " + expectActivityLaunch
2816                     + ", caller's uid " + Binder.getCallingUid()
2817                     + ", pid " + Binder.getCallingPid());
2818         }
2819         Preconditions.checkArgument(response != null, "response cannot be null");
2820         try {
2821             if (account == null) {
2822                 Slog.w(TAG, "getAuthToken called with null account");
2823                 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
2824                 return;
2825             }
2826             if (authTokenType == null) {
2827                 Slog.w(TAG, "getAuthToken called with null authTokenType");
2828                 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
2829                 return;
2830             }
2831         } catch (RemoteException e) {
2832             Slog.w(TAG, "Failed to report error back to the client." + e);
2833             return;
2834         }
2835         int userId = UserHandle.getCallingUserId();
2836         long ident = Binder.clearCallingIdentity();
2837         final UserAccounts accounts;
2838         final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
2839         try {
2840             accounts = getUserAccounts(userId);
2841             authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2842                     AuthenticatorDescription.newKey(account.type), accounts.userId);
2843         } finally {
2844             Binder.restoreCallingIdentity(ident);
2845         }
2846 
2847         final boolean customTokens =
2848                 authenticatorInfo != null && authenticatorInfo.type.customTokens;
2849 
2850         // skip the check if customTokens
2851         final int callerUid = Binder.getCallingUid();
2852         final boolean permissionGranted =
2853                 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
2854 
2855         // Get the calling package. We will use it for the purpose of caching.
2856         final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
2857         List<String> callerOwnedPackageNames;
2858         ident = Binder.clearCallingIdentity();
2859         try {
2860             callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
2861         } finally {
2862             Binder.restoreCallingIdentity(ident);
2863         }
2864         if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
2865             String msg = String.format(
2866                     "Uid %s is attempting to illegally masquerade as package %s!",
2867                     callerUid,
2868                     callerPkg);
2869             throw new SecurityException(msg);
2870         }
2871 
2872         // let authenticator know the identity of the caller
2873         loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
2874         loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
2875 
2876         if (notifyOnAuthFailure) {
2877             loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
2878         }
2879 
2880         long identityToken = clearCallingIdentity();
2881         try {
2882             // Distill the caller's package signatures into a single digest.
2883             final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
2884 
2885             // if the caller has permission, do the peek. otherwise go the more expensive
2886             // route of starting a Session
2887             if (!customTokens && permissionGranted) {
2888                 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
2889                 if (authToken != null) {
2890                     Bundle result = new Bundle();
2891                     result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
2892                     result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2893                     result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2894                     onResult(response, result);
2895                     return;
2896                 }
2897             }
2898 
2899             if (customTokens) {
2900                 /*
2901                  * Look up tokens in the new cache only if the loginOptions don't have parameters
2902                  * outside of those expected to be injected by the AccountManager, e.g.
2903                  * ANDORID_PACKAGE_NAME.
2904                  */
2905                 String token = readCachedTokenInternal(
2906                         accounts,
2907                         account,
2908                         authTokenType,
2909                         callerPkg,
2910                         callerPkgSigDigest);
2911                 if (token != null) {
2912                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
2913                         Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2914                     }
2915                     Bundle result = new Bundle();
2916                     result.putString(AccountManager.KEY_AUTHTOKEN, token);
2917                     result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2918                     result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2919                     onResult(response, result);
2920                     return;
2921                 }
2922             }
2923 
2924             new Session(
2925                     accounts,
2926                     response,
2927                     account.type,
2928                     expectActivityLaunch,
2929                     false /* stripAuthTokenFromResult */,
2930                     account.name,
2931                     false /* authDetailsRequired */) {
2932                 @Override
2933                 protected String toDebugString(long now) {
2934                     if (loginOptions != null) loginOptions.keySet();
2935                     return super.toDebugString(now) + ", getAuthToken"
2936                             + ", " + account.toSafeString()
2937                             + ", authTokenType " + authTokenType
2938                             + ", loginOptions " + loginOptions
2939                             + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2940                 }
2941 
2942                 @Override
2943                 public void run() throws RemoteException {
2944                     // If the caller doesn't have permission then create and return the
2945                     // "grant permission" intent instead of the "getAuthToken" intent.
2946                     if (!permissionGranted) {
2947                         mAuthenticator.getAuthTokenLabel(this, authTokenType);
2948                     } else {
2949                         mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2950                     }
2951                 }
2952 
2953                 @Override
2954                 public void onResult(Bundle result) {
2955                     Bundle.setDefusable(result, true);
2956                     if (result != null) {
2957                         if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
2958                             Intent intent = newGrantCredentialsPermissionIntent(
2959                                     account,
2960                                     null,
2961                                     callerUid,
2962                                     new AccountAuthenticatorResponse(this),
2963                                     authTokenType,
2964                                     true);
2965                             Bundle bundle = new Bundle();
2966                             bundle.putParcelable(AccountManager.KEY_INTENT, intent);
2967                             onResult(bundle);
2968                             return;
2969                         }
2970                         String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
2971                         if (authToken != null) {
2972                             String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2973                             String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
2974                             if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
2975                                 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
2976                                         "the type and name should not be empty");
2977                                 return;
2978                             }
2979                             Account resultAccount = new Account(name, type);
2980                             if (!customTokens) {
2981                                 saveAuthTokenToDatabase(
2982                                         mAccounts,
2983                                         resultAccount,
2984                                         authTokenType,
2985                                         authToken);
2986                             }
2987                             long expiryMillis = result.getLong(
2988                                     AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2989                             if (customTokens
2990                                     && expiryMillis > System.currentTimeMillis()) {
2991                                 saveCachedToken(
2992                                         mAccounts,
2993                                         account,
2994                                         callerPkg,
2995                                         callerPkgSigDigest,
2996                                         authTokenType,
2997                                         authToken,
2998                                         expiryMillis);
2999                             }
3000                         }
3001 
3002                         Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
3003                         if (intent != null && notifyOnAuthFailure && !customTokens) {
3004                             /*
3005                              * Make sure that the supplied intent is owned by the authenticator
3006                              * giving it to the system. Otherwise a malicious authenticator could
3007                              * have users launching arbitrary activities by tricking users to
3008                              * interact with malicious notifications.
3009                              */
3010                             if (!checkKeyIntent(
3011                                     Binder.getCallingUid(),
3012                                     intent)) {
3013                                 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
3014                                         "invalid intent in bundle returned");
3015                                 return;
3016                             }
3017                             doNotification(
3018                                     mAccounts,
3019                                     account,
3020                                     result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
3021                                     intent, "android", accounts.userId);
3022                         }
3023                     }
3024                     super.onResult(result);
3025                 }
3026             }.bind();
3027         } finally {
3028             restoreCallingIdentity(identityToken);
3029         }
3030     }
3031 
calculatePackageSignatureDigest(String callerPkg)3032     private byte[] calculatePackageSignatureDigest(String callerPkg) {
3033         MessageDigest digester;
3034         try {
3035             digester = MessageDigest.getInstance("SHA-256");
3036             PackageInfo pkgInfo = mPackageManager.getPackageInfo(
3037                     callerPkg, PackageManager.GET_SIGNATURES);
3038             for (Signature sig : pkgInfo.signatures) {
3039                 digester.update(sig.toByteArray());
3040             }
3041         } catch (NoSuchAlgorithmException x) {
3042             Log.wtf(TAG, "SHA-256 should be available", x);
3043             digester = null;
3044         } catch (NameNotFoundException e) {
3045             Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
3046             digester = null;
3047         }
3048         return (digester == null) ? null : digester.digest();
3049     }
3050 
createNoCredentialsPermissionNotification(Account account, Intent intent, String packageName, int userId)3051     private void createNoCredentialsPermissionNotification(Account account, Intent intent,
3052             String packageName, int userId) {
3053         int uid = intent.getIntExtra(
3054                 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
3055         String authTokenType = intent.getStringExtra(
3056                 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
3057         final String titleAndSubtitle =
3058                 mContext.getString(R.string.permission_request_notification_with_subtitle,
3059                 account.name);
3060         final int index = titleAndSubtitle.indexOf('\n');
3061         String title = titleAndSubtitle;
3062         String subtitle = "";
3063         if (index > 0) {
3064             title = titleAndSubtitle.substring(0, index);
3065             subtitle = titleAndSubtitle.substring(index + 1);
3066         }
3067         UserHandle user = UserHandle.of(userId);
3068         Context contextForUser = getContextForUser(user);
3069         Notification n =
3070                 new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT)
3071                     .setSmallIcon(android.R.drawable.stat_sys_warning)
3072                     .setWhen(0)
3073                     .setColor(contextForUser.getColor(
3074                             com.android.internal.R.color.system_notification_accent_color))
3075                     .setContentTitle(title)
3076                     .setContentText(subtitle)
3077                     .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
3078                             PendingIntent.FLAG_CANCEL_CURRENT, null, user))
3079                     .build();
3080         installNotification(getCredentialPermissionNotificationId(
3081                 account, authTokenType, uid), n, packageName, user.getIdentifier());
3082     }
3083 
newGrantCredentialsPermissionIntent(Account account, String packageName, int uid, AccountAuthenticatorResponse response, String authTokenType, boolean startInNewTask)3084     private Intent newGrantCredentialsPermissionIntent(Account account, String packageName,
3085             int uid, AccountAuthenticatorResponse response, String authTokenType,
3086             boolean startInNewTask) {
3087 
3088         Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
3089 
3090         if (startInNewTask) {
3091             // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
3092             // Since it was set in Eclair+ we can't change it without breaking apps using
3093             // the intent from a non-Activity context. This is the default behavior.
3094             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3095         }
3096         intent.addCategory(getCredentialPermissionNotificationId(account,
3097                 authTokenType, uid).mTag + (packageName != null ? packageName : ""));
3098         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
3099         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
3100         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
3101         intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
3102 
3103         return intent;
3104     }
3105 
getCredentialPermissionNotificationId(Account account, String authTokenType, int uid)3106     private NotificationId getCredentialPermissionNotificationId(Account account,
3107             String authTokenType, int uid) {
3108         NotificationId nId;
3109         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
3110         synchronized (accounts.credentialsPermissionNotificationIds) {
3111             final Pair<Pair<Account, String>, Integer> key =
3112                     new Pair<Pair<Account, String>, Integer>(
3113                             new Pair<Account, String>(account, authTokenType), uid);
3114             nId = accounts.credentialsPermissionNotificationIds.get(key);
3115             if (nId == null) {
3116                 String tag = TAG + ":" + SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION
3117                         + ":" + account.hashCode() + ":" + authTokenType.hashCode();
3118                 int id = SystemMessage.NOTE_ACCOUNT_CREDENTIAL_PERMISSION;
3119                 nId = new NotificationId(tag, id);
3120                 accounts.credentialsPermissionNotificationIds.put(key, nId);
3121             }
3122         }
3123         return nId;
3124     }
3125 
getSigninRequiredNotificationId(UserAccounts accounts, Account account)3126     private NotificationId getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
3127         NotificationId nId;
3128         synchronized (accounts.signinRequiredNotificationIds) {
3129             nId = accounts.signinRequiredNotificationIds.get(account);
3130             if (nId == null) {
3131                 String tag = TAG + ":" + SystemMessage.NOTE_ACCOUNT_REQUIRE_SIGNIN
3132                         + ":" + account.hashCode();
3133                 int id = SystemMessage.NOTE_ACCOUNT_REQUIRE_SIGNIN;
3134                 nId = new NotificationId(tag, id);
3135                 accounts.signinRequiredNotificationIds.put(account, nId);
3136             }
3137         }
3138         return nId;
3139     }
3140 
3141     @Override
addAccount(final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn)3142     public void addAccount(final IAccountManagerResponse response, final String accountType,
3143             final String authTokenType, final String[] requiredFeatures,
3144             final boolean expectActivityLaunch, final Bundle optionsIn) {
3145         Bundle.setDefusable(optionsIn, true);
3146         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3147             Log.v(TAG, "addAccount: accountType " + accountType
3148                     + ", response " + response
3149                     + ", authTokenType " + authTokenType
3150                     + ", requiredFeatures " + Arrays.toString(requiredFeatures)
3151                     + ", expectActivityLaunch " + expectActivityLaunch
3152                     + ", caller's uid " + Binder.getCallingUid()
3153                     + ", pid " + Binder.getCallingPid());
3154         }
3155         if (response == null) throw new IllegalArgumentException("response is null");
3156         if (accountType == null) throw new IllegalArgumentException("accountType is null");
3157 
3158         // Is user disallowed from modifying accounts?
3159         final int uid = Binder.getCallingUid();
3160         final int userId = UserHandle.getUserId(uid);
3161         if (!canUserModifyAccounts(userId, uid)) {
3162             try {
3163                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3164                         "User is not allowed to add an account!");
3165             } catch (RemoteException re) {
3166             }
3167             showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3168             return;
3169         }
3170         if (!canUserModifyAccountsForType(userId, accountType, uid)) {
3171             try {
3172                 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3173                         "User cannot modify accounts of this type (policy).");
3174             } catch (RemoteException re) {
3175             }
3176             showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3177                     userId);
3178             return;
3179         }
3180 
3181         final int pid = Binder.getCallingPid();
3182         final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3183         options.putInt(AccountManager.KEY_CALLER_UID, uid);
3184         options.putInt(AccountManager.KEY_CALLER_PID, pid);
3185 
3186         int usrId = UserHandle.getCallingUserId();
3187         long identityToken = clearCallingIdentity();
3188         try {
3189             UserAccounts accounts = getUserAccounts(usrId);
3190             logRecordWithUid(
3191                     accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
3192                     uid);
3193             new Session(accounts, response, accountType, expectActivityLaunch,
3194                     true /* stripAuthTokenFromResult */, null /* accountName */,
3195                     false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
3196                 @Override
3197                 public void run() throws RemoteException {
3198                     mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
3199                             options);
3200                 }
3201 
3202                 @Override
3203                 protected String toDebugString(long now) {
3204                     return super.toDebugString(now) + ", addAccount"
3205                             + ", accountType " + accountType
3206                             + ", requiredFeatures " + Arrays.toString(requiredFeatures);
3207                 }
3208             }.bind();
3209         } finally {
3210             restoreCallingIdentity(identityToken);
3211         }
3212     }
3213 
3214     @Override
addAccountAsUser(final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn, int userId)3215     public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
3216             final String authTokenType, final String[] requiredFeatures,
3217             final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
3218         Bundle.setDefusable(optionsIn, true);
3219         int callingUid = Binder.getCallingUid();
3220         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3221             Log.v(TAG, "addAccount: accountType " + accountType
3222                     + ", response " + response
3223                     + ", authTokenType " + authTokenType
3224                     + ", requiredFeatures " + Arrays.toString(requiredFeatures)
3225                     + ", expectActivityLaunch " + expectActivityLaunch
3226                     + ", caller's uid " + Binder.getCallingUid()
3227                     + ", pid " + Binder.getCallingPid()
3228                     + ", for user id " + userId);
3229         }
3230         Preconditions.checkArgument(response != null, "response cannot be null");
3231         Preconditions.checkArgument(accountType != null, "accountType cannot be null");
3232         // Only allow the system process to add accounts of other users
3233         if (isCrossUser(callingUid, userId)) {
3234             throw new SecurityException(
3235                     String.format(
3236                             "User %s trying to add account for %s" ,
3237                             UserHandle.getCallingUserId(),
3238                             userId));
3239         }
3240 
3241         // Is user disallowed from modifying accounts?
3242         if (!canUserModifyAccounts(userId, callingUid)) {
3243             try {
3244                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3245                         "User is not allowed to add an account!");
3246             } catch (RemoteException re) {
3247             }
3248             showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3249             return;
3250         }
3251         if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
3252             try {
3253                 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3254                         "User cannot modify accounts of this type (policy).");
3255             } catch (RemoteException re) {
3256             }
3257             showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3258                     userId);
3259             return;
3260         }
3261 
3262         final int pid = Binder.getCallingPid();
3263         final int uid = Binder.getCallingUid();
3264         final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3265         options.putInt(AccountManager.KEY_CALLER_UID, uid);
3266         options.putInt(AccountManager.KEY_CALLER_PID, pid);
3267 
3268         long identityToken = clearCallingIdentity();
3269         try {
3270             UserAccounts accounts = getUserAccounts(userId);
3271             logRecordWithUid(
3272                     accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
3273                     userId);
3274             new Session(accounts, response, accountType, expectActivityLaunch,
3275                     true /* stripAuthTokenFromResult */, null /* accountName */,
3276                     false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
3277                 @Override
3278                 public void run() throws RemoteException {
3279                     mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
3280                             options);
3281                 }
3282 
3283                 @Override
3284                 protected String toDebugString(long now) {
3285                     return super.toDebugString(now) + ", addAccount"
3286                             + ", accountType " + accountType
3287                             + ", requiredFeatures "
3288                             + (requiredFeatures != null
3289                               ? TextUtils.join(",", requiredFeatures)
3290                               : null);
3291                 }
3292             }.bind();
3293         } finally {
3294             restoreCallingIdentity(identityToken);
3295         }
3296     }
3297 
3298     @Override
startAddAccountSession( final IAccountManagerResponse response, final String accountType, final String authTokenType, final String[] requiredFeatures, final boolean expectActivityLaunch, final Bundle optionsIn)3299     public void startAddAccountSession(
3300             final IAccountManagerResponse response,
3301             final String accountType,
3302             final String authTokenType,
3303             final String[] requiredFeatures,
3304             final boolean expectActivityLaunch,
3305             final Bundle optionsIn) {
3306         Bundle.setDefusable(optionsIn, true);
3307         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3308             Log.v(TAG,
3309                     "startAddAccountSession: accountType " + accountType
3310                     + ", response " + response
3311                     + ", authTokenType " + authTokenType
3312                     + ", requiredFeatures " + Arrays.toString(requiredFeatures)
3313                     + ", expectActivityLaunch " + expectActivityLaunch
3314                     + ", caller's uid " + Binder.getCallingUid()
3315                     + ", pid " + Binder.getCallingPid());
3316         }
3317         Preconditions.checkArgument(response != null, "response cannot be null");
3318         Preconditions.checkArgument(accountType != null, "accountType cannot be null");
3319 
3320         final int uid = Binder.getCallingUid();
3321         final int userId = UserHandle.getUserId(uid);
3322         if (!canUserModifyAccounts(userId, uid)) {
3323             try {
3324                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3325                         "User is not allowed to add an account!");
3326             } catch (RemoteException re) {
3327             }
3328             showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3329             return;
3330         }
3331         if (!canUserModifyAccountsForType(userId, accountType, uid)) {
3332             try {
3333                 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3334                         "User cannot modify accounts of this type (policy).");
3335             } catch (RemoteException re) {
3336             }
3337             showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3338                     userId);
3339             return;
3340         }
3341         final int pid = Binder.getCallingPid();
3342         final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3343         options.putInt(AccountManager.KEY_CALLER_UID, uid);
3344         options.putInt(AccountManager.KEY_CALLER_PID, pid);
3345 
3346         // Check to see if the Password should be included to the caller.
3347         String callerPkg = options.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3348         boolean isPasswordForwardingAllowed = checkPermissionAndNote(
3349                 callerPkg, uid, Manifest.permission.GET_PASSWORD);
3350 
3351         long identityToken = clearCallingIdentity();
3352         try {
3353             UserAccounts accounts = getUserAccounts(userId);
3354             logRecordWithUid(accounts, AccountsDb.DEBUG_ACTION_CALLED_START_ACCOUNT_ADD,
3355                     AccountsDb.TABLE_ACCOUNTS, uid);
3356             new StartAccountSession(
3357                     accounts,
3358                     response,
3359                     accountType,
3360                     expectActivityLaunch,
3361                     null /* accountName */,
3362                     false /* authDetailsRequired */,
3363                     true /* updateLastAuthenticationTime */,
3364                     isPasswordForwardingAllowed) {
3365                 @Override
3366                 public void run() throws RemoteException {
3367                     mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
3368                             requiredFeatures, options);
3369                 }
3370 
3371                 @Override
3372                 protected String toDebugString(long now) {
3373                     String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
3374                     return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
3375                             + accountType + ", requiredFeatures "
3376                             + (requiredFeatures != null ? requiredFeaturesStr : null);
3377                 }
3378             }.bind();
3379         } finally {
3380             restoreCallingIdentity(identityToken);
3381         }
3382     }
3383 
3384     /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
3385     private abstract class StartAccountSession extends Session {
3386 
3387         private final boolean mIsPasswordForwardingAllowed;
3388 
StartAccountSession( UserAccounts accounts, IAccountManagerResponse response, String accountType, boolean expectActivityLaunch, String accountName, boolean authDetailsRequired, boolean updateLastAuthenticationTime, boolean isPasswordForwardingAllowed)3389         public StartAccountSession(
3390                 UserAccounts accounts,
3391                 IAccountManagerResponse response,
3392                 String accountType,
3393                 boolean expectActivityLaunch,
3394                 String accountName,
3395                 boolean authDetailsRequired,
3396                 boolean updateLastAuthenticationTime,
3397                 boolean isPasswordForwardingAllowed) {
3398             super(accounts, response, accountType, expectActivityLaunch,
3399                     true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
3400                     updateLastAuthenticationTime);
3401             mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
3402         }
3403 
3404         @Override
onResult(Bundle result)3405         public void onResult(Bundle result) {
3406             Bundle.setDefusable(result, true);
3407             mNumResults++;
3408             Intent intent = null;
3409             if (result != null
3410                     && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
3411                 if (!checkKeyIntent(
3412                         Binder.getCallingUid(),
3413                         intent)) {
3414                     onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
3415                             "invalid intent in bundle returned");
3416                     return;
3417                 }
3418             }
3419             IAccountManagerResponse response;
3420             if (mExpectActivityLaunch && result != null
3421                     && result.containsKey(AccountManager.KEY_INTENT)) {
3422                 response = mResponse;
3423             } else {
3424                 response = getResponseAndClose();
3425             }
3426             if (response == null) {
3427                 return;
3428             }
3429             if (result == null) {
3430                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3431                     Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
3432                             + response);
3433                 }
3434                 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3435                         "null bundle returned");
3436                 return;
3437             }
3438 
3439             if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
3440                 // All AccountManager error codes are greater
3441                 // than 0
3442                 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
3443                         result.getString(AccountManager.KEY_ERROR_MESSAGE));
3444                 return;
3445             }
3446 
3447             // Omit passwords if the caller isn't permitted to see them.
3448             if (!mIsPasswordForwardingAllowed) {
3449                 result.remove(AccountManager.KEY_PASSWORD);
3450             }
3451 
3452             // Strip auth token from result.
3453             result.remove(AccountManager.KEY_AUTHTOKEN);
3454 
3455             if (Log.isLoggable(TAG, Log.VERBOSE)) {
3456                 Log.v(TAG,
3457                         getClass().getSimpleName() + " calling onResult() on response " + response);
3458             }
3459 
3460             // Get the session bundle created by authenticator. The
3461             // bundle contains data necessary for finishing the session
3462             // later. The session bundle will be encrypted here and
3463             // decrypted later when trying to finish the session.
3464             Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
3465             if (sessionBundle != null) {
3466                 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3467                 if (TextUtils.isEmpty(accountType)
3468                         || !mAccountType.equalsIgnoreCase(accountType)) {
3469                     Log.w(TAG, "Account type in session bundle doesn't match request.");
3470                 }
3471                 // Add accountType info to session bundle. This will
3472                 // override any value set by authenticator.
3473                 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
3474 
3475                 // Encrypt session bundle before returning to caller.
3476                 try {
3477                     CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3478                     Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
3479                     result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
3480                 } catch (GeneralSecurityException e) {
3481                     if (Log.isLoggable(TAG, Log.DEBUG)) {
3482                         Log.v(TAG, "Failed to encrypt session bundle!", e);
3483                     }
3484                     sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3485                             "failed to encrypt session bundle");
3486                     return;
3487                 }
3488             }
3489 
3490             sendResponse(response, result);
3491         }
3492     }
3493 
3494     @Override
finishSessionAsUser(IAccountManagerResponse response, @NonNull Bundle sessionBundle, boolean expectActivityLaunch, Bundle appInfo, int userId)3495     public void finishSessionAsUser(IAccountManagerResponse response,
3496             @NonNull Bundle sessionBundle,
3497             boolean expectActivityLaunch,
3498             Bundle appInfo,
3499             int userId) {
3500         Bundle.setDefusable(sessionBundle, true);
3501         int callingUid = Binder.getCallingUid();
3502         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3503             Log.v(TAG,
3504                     "finishSession: response "+ response
3505                             + ", expectActivityLaunch " + expectActivityLaunch
3506                             + ", caller's uid " + callingUid
3507                             + ", caller's user id " + UserHandle.getCallingUserId()
3508                             + ", pid " + Binder.getCallingPid()
3509                             + ", for user id " + userId);
3510         }
3511         Preconditions.checkArgument(response != null, "response cannot be null");
3512         // Session bundle is the encrypted bundle of the original bundle created by authenticator.
3513         // Account type is added to it before encryption.
3514         if (sessionBundle == null || sessionBundle.size() == 0) {
3515             throw new IllegalArgumentException("sessionBundle is empty");
3516         }
3517 
3518         // Only allow the system process to finish session for other users.
3519         if (isCrossUser(callingUid, userId)) {
3520             throw new SecurityException(
3521                     String.format(
3522                             "User %s trying to finish session for %s without cross user permission",
3523                             UserHandle.getCallingUserId(),
3524                             userId));
3525         }
3526 
3527         if (!canUserModifyAccounts(userId, callingUid)) {
3528             sendErrorResponse(response,
3529                     AccountManager.ERROR_CODE_USER_RESTRICTED,
3530                     "User is not allowed to add an account!");
3531             showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3532             return;
3533         }
3534 
3535         final int pid = Binder.getCallingPid();
3536         final Bundle decryptedBundle;
3537         final String accountType;
3538         // First decrypt session bundle to get account type for checking permission.
3539         try {
3540             CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3541             decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
3542             if (decryptedBundle == null) {
3543                 sendErrorResponse(
3544                         response,
3545                         AccountManager.ERROR_CODE_BAD_REQUEST,
3546                         "failed to decrypt session bundle");
3547                 return;
3548             }
3549             accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3550             // Account type cannot be null. This should not happen if session bundle was created
3551             // properly by #StartAccountSession.
3552             if (TextUtils.isEmpty(accountType)) {
3553                 sendErrorResponse(
3554                         response,
3555                         AccountManager.ERROR_CODE_BAD_ARGUMENTS,
3556                         "accountType is empty");
3557                 return;
3558             }
3559 
3560             // If by any chances, decryptedBundle contains colliding keys with
3561             // system info
3562             // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
3563             // update credentials flow, we should replace with the new values of the current call.
3564             if (appInfo != null) {
3565                 decryptedBundle.putAll(appInfo);
3566             }
3567 
3568             // Add info that may be used by add account or update credentials flow.
3569             decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
3570             decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
3571         } catch (GeneralSecurityException e) {
3572             if (Log.isLoggable(TAG, Log.DEBUG)) {
3573                 Log.v(TAG, "Failed to decrypt session bundle!", e);
3574             }
3575             sendErrorResponse(
3576                     response,
3577                     AccountManager.ERROR_CODE_BAD_REQUEST,
3578                     "failed to decrypt session bundle");
3579             return;
3580         }
3581 
3582         if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
3583             sendErrorResponse(
3584                     response,
3585                     AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3586                     "User cannot modify accounts of this type (policy).");
3587             showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3588                     userId);
3589             return;
3590         }
3591 
3592         long identityToken = clearCallingIdentity();
3593         try {
3594             UserAccounts accounts = getUserAccounts(userId);
3595             logRecordWithUid(
3596                     accounts,
3597                     AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_SESSION_FINISH,
3598                     AccountsDb.TABLE_ACCOUNTS,
3599                     callingUid);
3600             new Session(
3601                     accounts,
3602                     response,
3603                     accountType,
3604                     expectActivityLaunch,
3605                     true /* stripAuthTokenFromResult */,
3606                     null /* accountName */,
3607                     false /* authDetailsRequired */,
3608                     true /* updateLastAuthenticationTime */) {
3609                 @Override
3610                 public void run() throws RemoteException {
3611                     mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
3612                 }
3613 
3614                 @Override
3615                 protected String toDebugString(long now) {
3616                     return super.toDebugString(now)
3617                             + ", finishSession"
3618                             + ", accountType " + accountType;
3619                 }
3620             }.bind();
3621         } finally {
3622             restoreCallingIdentity(identityToken);
3623         }
3624     }
3625 
showCantAddAccount(int errorCode, int userId)3626     private void showCantAddAccount(int errorCode, int userId) {
3627         final DevicePolicyManagerInternal dpmi =
3628                 LocalServices.getService(DevicePolicyManagerInternal.class);
3629         Intent intent = null;
3630         if (dpmi == null) {
3631             intent = getDefaultCantAddAccountIntent(errorCode);
3632         } else if (errorCode == AccountManager.ERROR_CODE_USER_RESTRICTED) {
3633             intent = dpmi.createUserRestrictionSupportIntent(userId,
3634                     UserManager.DISALLOW_MODIFY_ACCOUNTS);
3635         } else if (errorCode == AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
3636             intent = dpmi.createShowAdminSupportIntent(userId, false);
3637         }
3638         if (intent == null) {
3639             intent = getDefaultCantAddAccountIntent(errorCode);
3640         }
3641         long identityToken = clearCallingIdentity();
3642         try {
3643             mContext.startActivityAsUser(intent, new UserHandle(userId));
3644         } finally {
3645             restoreCallingIdentity(identityToken);
3646         }
3647     }
3648 
3649     /**
3650      * Called when we don't know precisely who is preventing us from adding an account.
3651      */
getDefaultCantAddAccountIntent(int errorCode)3652     private Intent getDefaultCantAddAccountIntent(int errorCode) {
3653         Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
3654         cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
3655         cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3656         return cantAddAccount;
3657     }
3658 
3659     @Override
confirmCredentialsAsUser( IAccountManagerResponse response, final Account account, final Bundle options, final boolean expectActivityLaunch, int userId)3660     public void confirmCredentialsAsUser(
3661             IAccountManagerResponse response,
3662             final Account account,
3663             final Bundle options,
3664             final boolean expectActivityLaunch,
3665             int userId) {
3666         Bundle.setDefusable(options, true);
3667         int callingUid = Binder.getCallingUid();
3668         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3669             Log.v(TAG, "confirmCredentials: " + account
3670                     + ", response " + response
3671                     + ", expectActivityLaunch " + expectActivityLaunch
3672                     + ", caller's uid " + callingUid
3673                     + ", pid " + Binder.getCallingPid());
3674         }
3675         // Only allow the system process to read accounts of other users
3676         if (isCrossUser(callingUid, userId)) {
3677             throw new SecurityException(
3678                     String.format(
3679                             "User %s trying to confirm account credentials for %s" ,
3680                             UserHandle.getCallingUserId(),
3681                             userId));
3682         }
3683         if (response == null) throw new IllegalArgumentException("response is null");
3684         if (account == null) throw new IllegalArgumentException("account is null");
3685         long identityToken = clearCallingIdentity();
3686         try {
3687             UserAccounts accounts = getUserAccounts(userId);
3688             new Session(accounts, response, account.type, expectActivityLaunch,
3689                     true /* stripAuthTokenFromResult */, account.name,
3690                     true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
3691                 @Override
3692                 public void run() throws RemoteException {
3693                     mAuthenticator.confirmCredentials(this, account, options);
3694                 }
3695                 @Override
3696                 protected String toDebugString(long now) {
3697                     return super.toDebugString(now) + ", confirmCredentials"
3698                             + ", " + account.toSafeString();
3699                 }
3700             }.bind();
3701         } finally {
3702             restoreCallingIdentity(identityToken);
3703         }
3704     }
3705 
3706     @Override
updateCredentials(IAccountManagerResponse response, final Account account, final String authTokenType, final boolean expectActivityLaunch, final Bundle loginOptions)3707     public void updateCredentials(IAccountManagerResponse response, final Account account,
3708             final String authTokenType, final boolean expectActivityLaunch,
3709             final Bundle loginOptions) {
3710         Bundle.setDefusable(loginOptions, true);
3711         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3712             Log.v(TAG, "updateCredentials: " + account
3713                     + ", response " + response
3714                     + ", authTokenType " + authTokenType
3715                     + ", expectActivityLaunch " + expectActivityLaunch
3716                     + ", caller's uid " + Binder.getCallingUid()
3717                     + ", pid " + Binder.getCallingPid());
3718         }
3719         if (response == null) throw new IllegalArgumentException("response is null");
3720         if (account == null) throw new IllegalArgumentException("account is null");
3721         int userId = UserHandle.getCallingUserId();
3722         long identityToken = clearCallingIdentity();
3723         try {
3724             UserAccounts accounts = getUserAccounts(userId);
3725             new Session(accounts, response, account.type, expectActivityLaunch,
3726                     true /* stripAuthTokenFromResult */, account.name,
3727                     false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
3728                 @Override
3729                 public void run() throws RemoteException {
3730                     mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
3731                 }
3732                 @Override
3733                 protected String toDebugString(long now) {
3734                     if (loginOptions != null) loginOptions.keySet();
3735                     return super.toDebugString(now) + ", updateCredentials"
3736                             + ", " + account.toSafeString()
3737                             + ", authTokenType " + authTokenType
3738                             + ", loginOptions " + loginOptions;
3739                 }
3740             }.bind();
3741         } finally {
3742             restoreCallingIdentity(identityToken);
3743         }
3744     }
3745 
3746     @Override
startUpdateCredentialsSession( IAccountManagerResponse response, final Account account, final String authTokenType, final boolean expectActivityLaunch, final Bundle loginOptions)3747     public void startUpdateCredentialsSession(
3748             IAccountManagerResponse response,
3749             final Account account,
3750             final String authTokenType,
3751             final boolean expectActivityLaunch,
3752             final Bundle loginOptions) {
3753         Bundle.setDefusable(loginOptions, true);
3754         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3755             Log.v(TAG,
3756                     "startUpdateCredentialsSession: " + account + ", response " + response
3757                             + ", authTokenType " + authTokenType + ", expectActivityLaunch "
3758                             + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
3759                             + ", pid " + Binder.getCallingPid());
3760         }
3761         if (response == null) {
3762             throw new IllegalArgumentException("response is null");
3763         }
3764         if (account == null) {
3765             throw new IllegalArgumentException("account is null");
3766         }
3767 
3768         final int uid = Binder.getCallingUid();
3769         int userId = UserHandle.getCallingUserId();
3770 
3771         // Check to see if the Password should be included to the caller.
3772         String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3773         boolean isPasswordForwardingAllowed = checkPermissionAndNote(
3774                 callerPkg, uid, Manifest.permission.GET_PASSWORD);
3775 
3776         long identityToken = clearCallingIdentity();
3777         try {
3778             UserAccounts accounts = getUserAccounts(userId);
3779             new StartAccountSession(
3780                     accounts,
3781                     response,
3782                     account.type,
3783                     expectActivityLaunch,
3784                     account.name,
3785                     false /* authDetailsRequired */,
3786                     true /* updateLastCredentialTime */,
3787                     isPasswordForwardingAllowed) {
3788                 @Override
3789                 public void run() throws RemoteException {
3790                     mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
3791                             loginOptions);
3792                 }
3793 
3794                 @Override
3795                 protected String toDebugString(long now) {
3796                     if (loginOptions != null)
3797                         loginOptions.keySet();
3798                     return super.toDebugString(now)
3799                             + ", startUpdateCredentialsSession"
3800                             + ", " + account.toSafeString()
3801                             + ", authTokenType " + authTokenType
3802                             + ", loginOptions " + loginOptions;
3803                 }
3804             }.bind();
3805         } finally {
3806             restoreCallingIdentity(identityToken);
3807         }
3808     }
3809 
3810     @Override
isCredentialsUpdateSuggested( IAccountManagerResponse response, final Account account, final String statusToken)3811     public void isCredentialsUpdateSuggested(
3812             IAccountManagerResponse response,
3813             final Account account,
3814             final String statusToken) {
3815         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3816             Log.v(TAG,
3817                     "isCredentialsUpdateSuggested: " + account + ", response " + response
3818                             + ", caller's uid " + Binder.getCallingUid()
3819                             + ", pid " + Binder.getCallingPid());
3820         }
3821         if (response == null) {
3822             throw new IllegalArgumentException("response is null");
3823         }
3824         if (account == null) {
3825             throw new IllegalArgumentException("account is null");
3826         }
3827         if (TextUtils.isEmpty(statusToken)) {
3828             throw new IllegalArgumentException("status token is empty");
3829         }
3830 
3831         int usrId = UserHandle.getCallingUserId();
3832         long identityToken = clearCallingIdentity();
3833         try {
3834             UserAccounts accounts = getUserAccounts(usrId);
3835             new Session(accounts, response, account.type, false /* expectActivityLaunch */,
3836                     false /* stripAuthTokenFromResult */, account.name,
3837                     false /* authDetailsRequired */) {
3838                 @Override
3839                 protected String toDebugString(long now) {
3840                     return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
3841                             + ", " + account.toSafeString();
3842                 }
3843 
3844                 @Override
3845                 public void run() throws RemoteException {
3846                     mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
3847                 }
3848 
3849                 @Override
3850                 public void onResult(Bundle result) {
3851                     Bundle.setDefusable(result, true);
3852                     IAccountManagerResponse response = getResponseAndClose();
3853                     if (response == null) {
3854                         return;
3855                     }
3856 
3857                     if (result == null) {
3858                         sendErrorResponse(
3859                                 response,
3860                                 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3861                                 "null bundle");
3862                         return;
3863                     }
3864 
3865                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
3866                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3867                                 + response);
3868                     }
3869                     // Check to see if an error occurred. We know if an error occurred because all
3870                     // error codes are greater than 0.
3871                     if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
3872                         sendErrorResponse(response,
3873                                 result.getInt(AccountManager.KEY_ERROR_CODE),
3874                                 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3875                         return;
3876                     }
3877                     if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
3878                         sendErrorResponse(
3879                                 response,
3880                                 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3881                                 "no result in response");
3882                         return;
3883                     }
3884                     final Bundle newResult = new Bundle();
3885                     newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
3886                             result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
3887                     sendResponse(response, newResult);
3888                 }
3889             }.bind();
3890         } finally {
3891             restoreCallingIdentity(identityToken);
3892         }
3893     }
3894 
3895     @Override
editProperties(IAccountManagerResponse response, final String accountType, final boolean expectActivityLaunch)3896     public void editProperties(IAccountManagerResponse response, final String accountType,
3897             final boolean expectActivityLaunch) {
3898         final int callingUid = Binder.getCallingUid();
3899         if (Log.isLoggable(TAG, Log.VERBOSE)) {
3900             Log.v(TAG, "editProperties: accountType " + accountType
3901                     + ", response " + response
3902                     + ", expectActivityLaunch " + expectActivityLaunch
3903                     + ", caller's uid " + callingUid
3904                     + ", pid " + Binder.getCallingPid());
3905         }
3906         if (response == null) throw new IllegalArgumentException("response is null");
3907         if (accountType == null) throw new IllegalArgumentException("accountType is null");
3908         int userId = UserHandle.getCallingUserId();
3909         if (!isAccountManagedByCaller(accountType, callingUid, userId)
3910                 && !isSystemUid(callingUid)) {
3911             String msg = String.format(
3912                     "uid %s cannot edit authenticator properites for account type: %s",
3913                     callingUid,
3914                     accountType);
3915             throw new SecurityException(msg);
3916         }
3917         long identityToken = clearCallingIdentity();
3918         try {
3919             UserAccounts accounts = getUserAccounts(userId);
3920             new Session(accounts, response, accountType, expectActivityLaunch,
3921                     true /* stripAuthTokenFromResult */, null /* accountName */,
3922                     false /* authDetailsRequired */) {
3923                 @Override
3924                 public void run() throws RemoteException {
3925                     mAuthenticator.editProperties(this, mAccountType);
3926                 }
3927                 @Override
3928                 protected String toDebugString(long now) {
3929                     return super.toDebugString(now) + ", editProperties"
3930                             + ", accountType " + accountType;
3931                 }
3932             }.bind();
3933         } finally {
3934             restoreCallingIdentity(identityToken);
3935         }
3936     }
3937 
3938     @Override
hasAccountAccess(@onNull Account account, @NonNull String packageName, @NonNull UserHandle userHandle)3939     public boolean hasAccountAccess(@NonNull Account account,  @NonNull String packageName,
3940             @NonNull UserHandle userHandle) {
3941         if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
3942             throw new SecurityException("Can be called only by system UID");
3943         }
3944         Preconditions.checkNotNull(account, "account cannot be null");
3945         Preconditions.checkNotNull(packageName, "packageName cannot be null");
3946         Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3947 
3948         final int userId = userHandle.getIdentifier();
3949 
3950         Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3951 
3952         try {
3953             int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3954             return hasAccountAccess(account, packageName, uid);
3955         } catch (NameNotFoundException e) {
3956             Log.d(TAG, "Package not found " + e.getMessage());
3957             return false;
3958         }
3959     }
3960 
3961     // Returns package with oldest target SDK for given UID.
getPackageNameForUid(int uid)3962     private String getPackageNameForUid(int uid) {
3963         String[] packageNames = mPackageManager.getPackagesForUid(uid);
3964         if (ArrayUtils.isEmpty(packageNames)) {
3965             return null;
3966         }
3967         String packageName = packageNames[0];
3968         if (packageNames.length == 1) {
3969             return packageName;
3970         }
3971         // Due to visibility changes we want to use package with oldest target SDK
3972         int oldestVersion = Integer.MAX_VALUE;
3973         for (String name : packageNames) {
3974             try {
3975                 ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(name, 0);
3976                 if (applicationInfo != null) {
3977                     int version = applicationInfo.targetSdkVersion;
3978                     if (version < oldestVersion) {
3979                         oldestVersion = version;
3980                         packageName = name;
3981                     }
3982                 }
3983             } catch (NameNotFoundException e) {
3984                 // skip
3985             }
3986         }
3987         return packageName;
3988     }
3989 
hasAccountAccess(@onNull Account account, @Nullable String packageName, int uid)3990     private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
3991             int uid) {
3992         if (packageName == null) {
3993             packageName = getPackageNameForUid(uid);
3994             if (packageName == null) {
3995                 return false;
3996             }
3997         }
3998 
3999         // Use null token which means any token. Having a token means the package
4000         // is trusted by the authenticator, hence it is fine to access the account.
4001         if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) {
4002             return true;
4003         }
4004         // In addition to the permissions required to get an auth token we also allow
4005         // the account to be accessed by apps for which user or authenticator granted visibility.
4006 
4007         int visibility = resolveAccountVisibility(account, packageName,
4008             getUserAccounts(UserHandle.getUserId(uid)));
4009         return (visibility == AccountManager.VISIBILITY_VISIBLE
4010             || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
4011     }
4012 
4013     @Override
createRequestAccountAccessIntentSenderAsUser(@onNull Account account, @NonNull String packageName, @NonNull UserHandle userHandle)4014     public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
4015             @NonNull String packageName, @NonNull UserHandle userHandle) {
4016         if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
4017             throw new SecurityException("Can be called only by system UID");
4018         }
4019 
4020         Preconditions.checkNotNull(account, "account cannot be null");
4021         Preconditions.checkNotNull(packageName, "packageName cannot be null");
4022         Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
4023 
4024         final int userId = userHandle.getIdentifier();
4025 
4026         Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
4027 
4028         final int uid;
4029         try {
4030             uid = mPackageManager.getPackageUidAsUser(packageName, userId);
4031         } catch (NameNotFoundException e) {
4032             Slog.e(TAG, "Unknown package " + packageName);
4033             return null;
4034         }
4035 
4036         Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
4037 
4038         final long identity = Binder.clearCallingIdentity();
4039         try {
4040             return PendingIntent.getActivityAsUser(
4041                     mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
4042                             | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
4043                     null, new UserHandle(userId)).getIntentSender();
4044         } finally {
4045             Binder.restoreCallingIdentity(identity);
4046         }
4047     }
4048 
newRequestAccountAccessIntent(Account account, String packageName, int uid, RemoteCallback callback)4049     private Intent newRequestAccountAccessIntent(Account account, String packageName,
4050             int uid, RemoteCallback callback) {
4051         return newGrantCredentialsPermissionIntent(account, packageName, uid,
4052                 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
4053             @Override
4054             public void onResult(Bundle value) throws RemoteException {
4055                 handleAuthenticatorResponse(true);
4056             }
4057 
4058             @Override
4059             public void onRequestContinued() {
4060                 /* ignore */
4061             }
4062 
4063             @Override
4064             public void onError(int errorCode, String errorMessage) throws RemoteException {
4065                 handleAuthenticatorResponse(false);
4066             }
4067 
4068             private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
4069                 cancelNotification(getCredentialPermissionNotificationId(account,
4070                         AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
4071                         UserHandle.getUserHandleForUid(uid));
4072                 if (callback != null) {
4073                     Bundle result = new Bundle();
4074                     result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
4075                     callback.sendResult(result);
4076                 }
4077             }
4078         }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false);
4079     }
4080 
4081     @Override
4082     public boolean someUserHasAccount(@NonNull final Account account) {
4083         if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
4084             throw new SecurityException("Only system can check for accounts across users");
4085         }
4086         final long token = Binder.clearCallingIdentity();
4087         try {
4088             AccountAndUser[] allAccounts = getAllAccounts();
4089             for (int i = allAccounts.length - 1; i >= 0; i--) {
4090                 if (allAccounts[i].account.equals(account)) {
4091                     return true;
4092                 }
4093             }
4094             return false;
4095         } finally {
4096             Binder.restoreCallingIdentity(token);
4097         }
4098     }
4099 
4100     private class GetAccountsByTypeAndFeatureSession extends Session {
4101         private final String[] mFeatures;
4102         private volatile Account[] mAccountsOfType = null;
4103         private volatile ArrayList<Account> mAccountsWithFeatures = null;
4104         private volatile int mCurrentAccount = 0;
4105         private final int mCallingUid;
4106         private final String mPackageName;
4107         private final boolean mIncludeManagedNotVisible;
4108 
4109         public GetAccountsByTypeAndFeatureSession(
4110                 UserAccounts accounts,
4111                 IAccountManagerResponse response,
4112                 String type,
4113                 String[] features,
4114                 int callingUid,
4115                 String packageName,
4116                 boolean includeManagedNotVisible) {
4117             super(accounts, response, type, false /* expectActivityLaunch */,
4118                     true /* stripAuthTokenFromResult */, null /* accountName */,
4119                     false /* authDetailsRequired */);
4120             mCallingUid = callingUid;
4121             mFeatures = features;
4122             mPackageName = packageName;
4123             mIncludeManagedNotVisible = includeManagedNotVisible;
4124         }
4125 
4126         @Override
4127         public void run() throws RemoteException {
4128             mAccountsOfType = getAccountsFromCache(mAccounts, mAccountType,
4129                     mCallingUid, mPackageName, mIncludeManagedNotVisible);
4130             // check whether each account matches the requested features
4131             mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
4132             mCurrentAccount = 0;
4133 
4134             checkAccount();
4135         }
4136 
4137         public void checkAccount() {
4138             if (mCurrentAccount >= mAccountsOfType.length) {
4139                 sendResult();
4140                 return;
4141             }
4142 
4143             final IAccountAuthenticator accountAuthenticator = mAuthenticator;
4144             if (accountAuthenticator == null) {
4145                 // It is possible that the authenticator has died, which is indicated by
4146                 // mAuthenticator being set to null. If this happens then just abort.
4147                 // There is no need to send back a result or error in this case since
4148                 // that already happened when mAuthenticator was cleared.
4149                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4150                     Log.v(TAG, "checkAccount: aborting session since we are no longer"
4151                             + " connected to the authenticator, " + toDebugString());
4152                 }
4153                 return;
4154             }
4155             try {
4156                 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
4157             } catch (RemoteException e) {
4158                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
4159             }
4160         }
4161 
4162         @Override
4163         public void onResult(Bundle result) {
4164             Bundle.setDefusable(result, true);
4165             mNumResults++;
4166             if (result == null) {
4167                 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
4168                 return;
4169             }
4170             if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
4171                 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
4172             }
4173             mCurrentAccount++;
4174             checkAccount();
4175         }
4176 
4177         public void sendResult() {
4178             IAccountManagerResponse response = getResponseAndClose();
4179             if (response != null) {
4180                 try {
4181                     Account[] accounts = new Account[mAccountsWithFeatures.size()];
4182                     for (int i = 0; i < accounts.length; i++) {
4183                         accounts[i] = mAccountsWithFeatures.get(i);
4184                     }
4185                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
4186                         Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
4187                                 + response);
4188                     }
4189                     Bundle result = new Bundle();
4190                     result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4191                     response.onResult(result);
4192                 } catch (RemoteException e) {
4193                     // if the caller is dead then there is no one to care about remote exceptions
4194                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
4195                         Log.v(TAG, "failure while notifying response", e);
4196                     }
4197                 }
4198             }
4199         }
4200 
4201         @Override
4202         protected String toDebugString(long now) {
4203             return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
4204                     + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
4205         }
4206     }
4207 
4208     /**
4209      * Returns the accounts visible to the client within the context of a specific user
4210      * @hide
4211      */
4212     @NonNull
4213     public Account[] getAccounts(int userId, String opPackageName) {
4214         int callingUid = Binder.getCallingUid();
4215         mAppOpsManager.checkPackage(callingUid, opPackageName);
4216         List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4217                 opPackageName);
4218         if (visibleAccountTypes.isEmpty()) {
4219             return EMPTY_ACCOUNT_ARRAY;
4220         }
4221         long identityToken = clearCallingIdentity();
4222         try {
4223             UserAccounts accounts = getUserAccounts(userId);
4224             return getAccountsInternal(
4225                     accounts,
4226                     callingUid,
4227                     opPackageName,
4228                     visibleAccountTypes,
4229                     false /* includeUserManagedNotVisible */);
4230         } finally {
4231             restoreCallingIdentity(identityToken);
4232         }
4233     }
4234 
4235     /**
4236      * Returns accounts for all running users, ignores visibility values.
4237      *
4238      * @hide
4239      */
4240     @NonNull
4241     public AccountAndUser[] getRunningAccounts() {
4242         final int[] runningUserIds;
4243         try {
4244             runningUserIds = ActivityManager.getService().getRunningUserIds();
4245         } catch (RemoteException e) {
4246             // Running in system_server; should never happen
4247             throw new RuntimeException(e);
4248         }
4249         return getAccounts(runningUserIds);
4250     }
4251 
4252     /**
4253      * Returns accounts for all users, ignores visibility values.
4254      *
4255      * @hide
4256      */
4257     @NonNull
4258     public AccountAndUser[] getAllAccounts() {
4259         final List<UserInfo> users = getUserManager().getUsers(true);
4260         final int[] userIds = new int[users.size()];
4261         for (int i = 0; i < userIds.length; i++) {
4262             userIds[i] = users.get(i).id;
4263         }
4264         return getAccounts(userIds);
4265     }
4266 
4267     @NonNull
4268     private AccountAndUser[] getAccounts(int[] userIds) {
4269         final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
4270         for (int userId : userIds) {
4271             UserAccounts userAccounts = getUserAccounts(userId);
4272             if (userAccounts == null) continue;
4273             Account[] accounts = getAccountsFromCache(
4274                     userAccounts,
4275                     null /* type */,
4276                     Binder.getCallingUid(),
4277                     null /* packageName */,
4278                     false /* include managed not visible*/);
4279             for (Account account : accounts) {
4280                 runningAccounts.add(new AccountAndUser(account, userId));
4281             }
4282         }
4283 
4284         AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
4285         return runningAccounts.toArray(accountsArray);
4286     }
4287 
4288     @Override
4289     @NonNull
4290     public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
4291         int callingUid = Binder.getCallingUid();
4292         mAppOpsManager.checkPackage(callingUid, opPackageName);
4293         return getAccountsAsUserForPackage(type, userId, opPackageName /* callingPackage */, -1,
4294                 opPackageName, false /* includeUserManagedNotVisible */);
4295     }
4296 
4297     @NonNull
4298     private Account[] getAccountsAsUserForPackage(
4299             String type,
4300             int userId,
4301             String callingPackage,
4302             int packageUid,
4303             String opPackageName,
4304             boolean includeUserManagedNotVisible) {
4305         int callingUid = Binder.getCallingUid();
4306         // Only allow the system process to read accounts of other users
4307         if (userId != UserHandle.getCallingUserId()
4308                 && callingUid != Process.SYSTEM_UID
4309                 && mContext.checkCallingOrSelfPermission(
4310                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
4311                     != PackageManager.PERMISSION_GRANTED) {
4312             throw new SecurityException("User " + UserHandle.getCallingUserId()
4313                     + " trying to get account for " + userId);
4314         }
4315 
4316         if (Log.isLoggable(TAG, Log.VERBOSE)) {
4317             Log.v(TAG, "getAccounts: accountType " + type
4318                     + ", caller's uid " + Binder.getCallingUid()
4319                     + ", pid " + Binder.getCallingPid());
4320         }
4321 
4322         // If the original calling app was using account choosing activity
4323         // provided by the framework or authenticator we'll passing in
4324         // the original caller's uid here, which is what should be used for filtering.
4325         List<String> managedTypes =
4326                 getTypesManagedByCaller(callingUid, UserHandle.getUserId(callingUid));
4327         if (packageUid != -1 &&
4328                 ((UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
4329                 || (type != null && managedTypes.contains(type))))) {
4330             callingUid = packageUid;
4331             opPackageName = callingPackage;
4332         }
4333         List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4334                 opPackageName);
4335         if (visibleAccountTypes.isEmpty()
4336                 || (type != null && !visibleAccountTypes.contains(type))) {
4337             return EMPTY_ACCOUNT_ARRAY;
4338         } else if (visibleAccountTypes.contains(type)) {
4339             // Prune the list down to just the requested type.
4340             visibleAccountTypes = new ArrayList<>();
4341             visibleAccountTypes.add(type);
4342         } // else aggregate all the visible accounts (it won't matter if the
4343           // list is empty).
4344 
4345         long identityToken = clearCallingIdentity();
4346         try {
4347             UserAccounts accounts = getUserAccounts(userId);
4348             return getAccountsInternal(
4349                     accounts,
4350                     callingUid,
4351                     opPackageName,
4352                     visibleAccountTypes,
4353                     includeUserManagedNotVisible);
4354         } finally {
4355             restoreCallingIdentity(identityToken);
4356         }
4357     }
4358 
4359     @NonNull
4360     private Account[] getAccountsInternal(
4361             UserAccounts userAccounts,
4362             int callingUid,
4363             String callingPackage,
4364             List<String> visibleAccountTypes,
4365             boolean includeUserManagedNotVisible) {
4366         ArrayList<Account> visibleAccounts = new ArrayList<>();
4367         for (String visibleType : visibleAccountTypes) {
4368             Account[] accountsForType = getAccountsFromCache(
4369                     userAccounts, visibleType, callingUid, callingPackage,
4370                     includeUserManagedNotVisible);
4371             if (accountsForType != null) {
4372                 visibleAccounts.addAll(Arrays.asList(accountsForType));
4373             }
4374         }
4375         Account[] result = new Account[visibleAccounts.size()];
4376         for (int i = 0; i < visibleAccounts.size(); i++) {
4377             result[i] = visibleAccounts.get(i);
4378         }
4379         return result;
4380     }
4381 
4382     @Override
4383     public void addSharedAccountsFromParentUser(int parentUserId, int userId,
4384             String opPackageName) {
4385         checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
4386         Account[] accounts = getAccountsAsUser(null, parentUserId, opPackageName);
4387         for (Account account : accounts) {
4388             addSharedAccountAsUser(account, userId);
4389         }
4390     }
4391 
4392     private boolean addSharedAccountAsUser(Account account, int userId) {
4393         userId = handleIncomingUser(userId);
4394         UserAccounts accounts = getUserAccounts(userId);
4395         accounts.accountsDb.deleteSharedAccount(account);
4396         long accountId = accounts.accountsDb.insertSharedAccount(account);
4397         if (accountId < 0) {
4398             Log.w(TAG, "insertAccountIntoDatabase: " + account.toSafeString()
4399                     + ", skipping the DB insert failed");
4400             return false;
4401         }
4402         logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_SHARED_ACCOUNTS, accountId,
4403                 accounts);
4404         return true;
4405     }
4406 
4407     public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
4408         userId = handleIncomingUser(userId);
4409         UserAccounts accounts = getUserAccounts(userId);
4410         long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account);
4411         int r = accounts.accountsDb.renameSharedAccount(account, newName);
4412         if (r > 0) {
4413             int callingUid = getCallingUid();
4414             logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_RENAME, AccountsDb.TABLE_SHARED_ACCOUNTS,
4415                     sharedTableAccountId, accounts, callingUid);
4416             // Recursively rename the account.
4417             renameAccountInternal(accounts, account, newName);
4418         }
4419         return r > 0;
4420     }
4421 
4422     public boolean removeSharedAccountAsUser(Account account, int userId) {
4423         return removeSharedAccountAsUser(account, userId, getCallingUid());
4424     }
4425 
4426     private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
4427         userId = handleIncomingUser(userId);
4428         UserAccounts accounts = getUserAccounts(userId);
4429         long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account);
4430         boolean deleted = accounts.accountsDb.deleteSharedAccount(account);
4431         if (deleted) {
4432             logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE, AccountsDb.TABLE_SHARED_ACCOUNTS,
4433                     sharedTableAccountId, accounts, callingUid);
4434             removeAccountInternal(accounts, account, callingUid);
4435         }
4436         return deleted;
4437     }
4438 
4439     public Account[] getSharedAccountsAsUser(int userId) {
4440         userId = handleIncomingUser(userId);
4441         UserAccounts accounts = getUserAccounts(userId);
4442         synchronized (accounts.dbLock) {
4443             List<Account> accountList = accounts.accountsDb.getSharedAccounts();
4444             Account[] accountArray = new Account[accountList.size()];
4445             accountList.toArray(accountArray);
4446             return accountArray;
4447         }
4448     }
4449 
4450     @Override
4451     @NonNull
4452     public Account[] getAccounts(String type, String opPackageName) {
4453         return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
4454     }
4455 
4456     @Override
4457     @NonNull
4458     public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
4459         int callingUid = Binder.getCallingUid();
4460         if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)) {
4461             // Don't do opPackageName check - caller is system.
4462             throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
4463                     + callingUid + " with uid=" + uid);
4464         }
4465         return getAccountsAsUserForPackage(null, UserHandle.getCallingUserId(), packageName, uid,
4466                 opPackageName, true /* includeUserManagedNotVisible */);
4467     }
4468 
4469     @Override
4470     @NonNull
4471     public Account[] getAccountsByTypeForPackage(String type, String packageName,
4472             String opPackageName) {
4473         int callingUid =  Binder.getCallingUid();
4474         int userId = UserHandle.getCallingUserId();
4475         mAppOpsManager.checkPackage(callingUid, opPackageName);
4476         int packageUid = -1;
4477         try {
4478             packageUid = mPackageManager.getPackageUidAsUser(packageName, userId);
4479         } catch (NameNotFoundException re) {
4480             Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
4481             return EMPTY_ACCOUNT_ARRAY;
4482         }
4483         if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
4484                 && (type != null && !isAccountManagedByCaller(type, callingUid, userId))) {
4485                 return EMPTY_ACCOUNT_ARRAY;
4486         }
4487         if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) && type == null) {
4488             return getAccountsAsUserForPackage(type, userId,
4489                 packageName, packageUid, opPackageName, false /* includeUserManagedNotVisible */);
4490         }
4491         return getAccountsAsUserForPackage(type, userId,
4492                 packageName, packageUid, opPackageName, true /* includeUserManagedNotVisible */);
4493     }
4494 
4495     private boolean needToStartChooseAccountActivity(Account[] accounts, String callingPackage) {
4496         if (accounts.length < 1) return false;
4497         if (accounts.length > 1) return true;
4498         Account account = accounts[0];
4499         UserAccounts userAccounts = getUserAccounts(UserHandle.getCallingUserId());
4500         int visibility = resolveAccountVisibility(account, callingPackage, userAccounts);
4501         if (visibility == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE) return true;
4502         return false;
4503     }
4504 
4505     private void startChooseAccountActivityWithAccounts(
4506         IAccountManagerResponse response, Account[] accounts, String callingPackage) {
4507         Intent intent = new Intent(mContext, ChooseAccountActivity.class);
4508         intent.putExtra(AccountManager.KEY_ACCOUNTS, accounts);
4509         intent.putExtra(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE,
4510                 new AccountManagerResponse(response));
4511         intent.putExtra(AccountManager.KEY_ANDROID_PACKAGE_NAME, callingPackage);
4512 
4513         mContext.startActivityAsUser(intent, UserHandle.of(UserHandle.getCallingUserId()));
4514     }
4515 
4516     private void handleGetAccountsResult(
4517         IAccountManagerResponse response,
4518         Account[] accounts,
4519         String callingPackage) {
4520 
4521         if (needToStartChooseAccountActivity(accounts, callingPackage)) {
4522             startChooseAccountActivityWithAccounts(response, accounts, callingPackage);
4523             return;
4524         }
4525         if (accounts.length == 1) {
4526             Bundle bundle = new Bundle();
4527             bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accounts[0].name);
4528             bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accounts[0].type);
4529             onResult(response, bundle);
4530             return;
4531         }
4532         // No qualified account exists, return an empty Bundle.
4533         onResult(response, new Bundle());
4534     }
4535 
4536     @Override
4537     public void getAccountByTypeAndFeatures(
4538         IAccountManagerResponse response,
4539         String accountType,
4540         String[] features,
4541         String opPackageName) {
4542 
4543         int callingUid = Binder.getCallingUid();
4544         mAppOpsManager.checkPackage(callingUid, opPackageName);
4545         if (Log.isLoggable(TAG, Log.VERBOSE)) {
4546             Log.v(TAG, "getAccount: accountType " + accountType
4547                     + ", response " + response
4548                     + ", features " + Arrays.toString(features)
4549                     + ", caller's uid " + callingUid
4550                     + ", pid " + Binder.getCallingPid());
4551         }
4552         if (response == null) throw new IllegalArgumentException("response is null");
4553         if (accountType == null) throw new IllegalArgumentException("accountType is null");
4554 
4555         int userId = UserHandle.getCallingUserId();
4556 
4557         long identityToken = clearCallingIdentity();
4558         try {
4559             UserAccounts userAccounts = getUserAccounts(userId);
4560             if (ArrayUtils.isEmpty(features)) {
4561                 Account[] accountsWithManagedNotVisible = getAccountsFromCache(
4562                     userAccounts, accountType, callingUid, opPackageName,
4563                     true /* include managed not visible */);
4564                 handleGetAccountsResult(
4565                     response, accountsWithManagedNotVisible, opPackageName);
4566                 return;
4567             }
4568 
4569             IAccountManagerResponse retrieveAccountsResponse =
4570                 new IAccountManagerResponse.Stub() {
4571                 @Override
4572                 public void onResult(Bundle value) throws RemoteException {
4573                     Parcelable[] parcelables = value.getParcelableArray(
4574                         AccountManager.KEY_ACCOUNTS);
4575                     Account[] accounts = new Account[parcelables.length];
4576                     for (int i = 0; i < parcelables.length; i++) {
4577                         accounts[i] = (Account) parcelables[i];
4578                     }
4579                     handleGetAccountsResult(
4580                         response, accounts, opPackageName);
4581                 }
4582 
4583                 @Override
4584                 public void onError(int errorCode, String errorMessage)
4585                         throws RemoteException {
4586                     // Will not be called in this case.
4587                 }
4588             };
4589             new GetAccountsByTypeAndFeatureSession(
4590                     userAccounts,
4591                     retrieveAccountsResponse,
4592                     accountType,
4593                     features,
4594                     callingUid,
4595                     opPackageName,
4596                     true /* include managed not visible */).bind();
4597         } finally {
4598             restoreCallingIdentity(identityToken);
4599         }
4600     }
4601 
4602     @Override
4603     public void getAccountsByFeatures(
4604             IAccountManagerResponse response,
4605             String type,
4606             String[] features,
4607             String opPackageName) {
4608         int callingUid = Binder.getCallingUid();
4609         mAppOpsManager.checkPackage(callingUid, opPackageName);
4610         if (Log.isLoggable(TAG, Log.VERBOSE)) {
4611             Log.v(TAG, "getAccounts: accountType " + type
4612                     + ", response " + response
4613                     + ", features " + Arrays.toString(features)
4614                     + ", caller's uid " + callingUid
4615                     + ", pid " + Binder.getCallingPid());
4616         }
4617         if (response == null) throw new IllegalArgumentException("response is null");
4618         if (type == null) throw new IllegalArgumentException("accountType is null");
4619         int userId = UserHandle.getCallingUserId();
4620 
4621         List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4622                 opPackageName);
4623         if (!visibleAccountTypes.contains(type)) {
4624             Bundle result = new Bundle();
4625             // Need to return just the accounts that are from matching signatures.
4626             result.putParcelableArray(AccountManager.KEY_ACCOUNTS, EMPTY_ACCOUNT_ARRAY);
4627             try {
4628                 response.onResult(result);
4629             } catch (RemoteException e) {
4630                 Log.e(TAG, "Cannot respond to caller do to exception." , e);
4631             }
4632             return;
4633         }
4634 
4635         long identityToken = clearCallingIdentity();
4636         try {
4637             UserAccounts userAccounts = getUserAccounts(userId);
4638             if (features == null || features.length == 0) {
4639                 Account[] accounts = getAccountsFromCache(userAccounts, type, callingUid,
4640                         opPackageName, false);
4641                 Bundle result = new Bundle();
4642                 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4643                 onResult(response, result);
4644                 return;
4645             }
4646             new GetAccountsByTypeAndFeatureSession(
4647                     userAccounts,
4648                     response,
4649                     type,
4650                     features,
4651                     callingUid,
4652                     opPackageName,
4653                     false /* include managed not visible */).bind();
4654         } finally {
4655             restoreCallingIdentity(identityToken);
4656         }
4657     }
4658 
4659     @Override
4660     public void onAccountAccessed(String token) throws RemoteException {
4661         final int uid = Binder.getCallingUid();
4662         if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
4663             return;
4664         }
4665         final int userId = UserHandle.getCallingUserId();
4666         final long identity = Binder.clearCallingIdentity();
4667         try {
4668             for (Account account : getAccounts(userId, mContext.getOpPackageName())) {
4669                 if (Objects.equals(account.getAccessId(), token)) {
4670                     // An app just accessed the account. At this point it knows about
4671                     // it and there is not need to hide this account from the app.
4672                     // Do we need to update account visibility here?
4673                     if (!hasAccountAccess(account, null, uid)) {
4674                         updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
4675                                 uid, true);
4676                     }
4677                 }
4678             }
4679         } finally {
4680             Binder.restoreCallingIdentity(identity);
4681         }
4682     }
4683 
4684     @Override
4685     public void onShellCommand(FileDescriptor in, FileDescriptor out,
4686             FileDescriptor err, String[] args, ShellCallback callback,
4687             ResultReceiver resultReceiver) {
4688         new AccountManagerServiceShellCommand(this).exec(this, in, out, err, args,
4689                 callback, resultReceiver);
4690     }
4691 
4692     private abstract class Session extends IAccountAuthenticatorResponse.Stub
4693             implements IBinder.DeathRecipient, ServiceConnection {
4694         IAccountManagerResponse mResponse;
4695         final String mAccountType;
4696         final boolean mExpectActivityLaunch;
4697         final long mCreationTime;
4698         final String mAccountName;
4699         // Indicates if we need to add auth details(like last credential time)
4700         final boolean mAuthDetailsRequired;
4701         // If set, we need to update the last authenticated time. This is
4702         // currently
4703         // used on
4704         // successful confirming credentials.
4705         final boolean mUpdateLastAuthenticatedTime;
4706 
4707         public int mNumResults = 0;
4708         private int mNumRequestContinued = 0;
4709         private int mNumErrors = 0;
4710 
4711         IAccountAuthenticator mAuthenticator = null;
4712 
4713         private final boolean mStripAuthTokenFromResult;
4714         protected final UserAccounts mAccounts;
4715 
4716         public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4717                 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4718                 boolean authDetailsRequired) {
4719             this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4720                     accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4721         }
4722 
4723         public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4724                 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4725                 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
4726             super();
4727             //if (response == null) throw new IllegalArgumentException("response is null");
4728             if (accountType == null) throw new IllegalArgumentException("accountType is null");
4729             mAccounts = accounts;
4730             mStripAuthTokenFromResult = stripAuthTokenFromResult;
4731             mResponse = response;
4732             mAccountType = accountType;
4733             mExpectActivityLaunch = expectActivityLaunch;
4734             mCreationTime = SystemClock.elapsedRealtime();
4735             mAccountName = accountName;
4736             mAuthDetailsRequired = authDetailsRequired;
4737             mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
4738 
4739             synchronized (mSessions) {
4740                 mSessions.put(toString(), this);
4741             }
4742             if (response != null) {
4743                 try {
4744                     response.asBinder().linkToDeath(this, 0 /* flags */);
4745                 } catch (RemoteException e) {
4746                     mResponse = null;
4747                     binderDied();
4748                 }
4749             }
4750         }
4751 
4752         IAccountManagerResponse getResponseAndClose() {
4753             if (mResponse == null) {
4754                 // this session has already been closed
4755                 return null;
4756             }
4757             IAccountManagerResponse response = mResponse;
4758             close(); // this clears mResponse so we need to save the response before this call
4759             return response;
4760         }
4761 
4762         /**
4763          * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4764          * security policy.
4765          *
4766          * In particular we want to make sure that the Authenticator doesn't try to trick users
4767          * into launching arbitrary intents on the device via by tricking to click authenticator
4768          * supplied entries in the system Settings app.
4769          */
4770          protected boolean checkKeyIntent(int authUid, Intent intent) {
4771             intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION
4772                     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
4773                     | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
4774                     | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION));
4775             long bid = Binder.clearCallingIdentity();
4776             try {
4777                 PackageManager pm = mContext.getPackageManager();
4778                 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4779                 if (resolveInfo == null) {
4780                     return false;
4781                 }
4782                 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4783                 int targetUid = targetActivityInfo.applicationInfo.uid;
4784                 PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
4785                 if (!isExportedSystemActivity(targetActivityInfo)
4786                         && !pmi.hasSignatureCapability(
4787                                 targetUid, authUid,
4788                                 PackageParser.SigningDetails.CertCapabilities.AUTH)) {
4789                     String pkgName = targetActivityInfo.packageName;
4790                     String activityName = targetActivityInfo.name;
4791                     String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4792                             + "does not share a signature with the supplying authenticator (%s).";
4793                     Log.e(TAG, String.format(tmpl, activityName, pkgName, mAccountType));
4794                     return false;
4795                 }
4796                 return true;
4797             } finally {
4798                 Binder.restoreCallingIdentity(bid);
4799             }
4800         }
4801 
4802         private boolean isExportedSystemActivity(ActivityInfo activityInfo) {
4803             String className = activityInfo.name;
4804             return "android".equals(activityInfo.packageName) &&
4805                     (GrantCredentialsPermissionActivity.class.getName().equals(className)
4806                     || CantAddAccountActivity.class.getName().equals(className));
4807         }
4808 
4809         private void close() {
4810             synchronized (mSessions) {
4811                 if (mSessions.remove(toString()) == null) {
4812                     // the session was already closed, so bail out now
4813                     return;
4814                 }
4815             }
4816             if (mResponse != null) {
4817                 // stop listening for response deaths
4818                 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4819 
4820                 // clear this so that we don't accidentally send any further results
4821                 mResponse = null;
4822             }
4823             cancelTimeout();
4824             unbind();
4825         }
4826 
4827         @Override
4828         public void binderDied() {
4829             mResponse = null;
4830             close();
4831         }
4832 
4833         protected String toDebugString() {
4834             return toDebugString(SystemClock.elapsedRealtime());
4835         }
4836 
4837         protected String toDebugString(long now) {
4838             return "Session: expectLaunch " + mExpectActivityLaunch
4839                     + ", connected " + (mAuthenticator != null)
4840                     + ", stats (" + mNumResults + "/" + mNumRequestContinued
4841                     + "/" + mNumErrors + ")"
4842                     + ", lifetime " + ((now - mCreationTime) / 1000.0);
4843         }
4844 
4845         void bind() {
4846             if (Log.isLoggable(TAG, Log.VERBOSE)) {
4847                 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4848             }
4849             if (!bindToAuthenticator(mAccountType)) {
4850                 Log.d(TAG, "bind attempt failed for " + toDebugString());
4851                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
4852             }
4853         }
4854 
4855         private void unbind() {
4856             if (mAuthenticator != null) {
4857                 mAuthenticator = null;
4858                 mContext.unbindService(this);
4859             }
4860         }
4861 
4862         public void cancelTimeout() {
4863             mHandler.removeMessages(MESSAGE_TIMED_OUT, this);
4864         }
4865 
4866         @Override
4867         public void onServiceConnected(ComponentName name, IBinder service) {
4868             mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
4869             try {
4870                 run();
4871             } catch (RemoteException e) {
4872                 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4873                         "remote exception");
4874             }
4875         }
4876 
4877         @Override
4878         public void onServiceDisconnected(ComponentName name) {
4879             mAuthenticator = null;
4880             IAccountManagerResponse response = getResponseAndClose();
4881             if (response != null) {
4882                 try {
4883                     response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4884                             "disconnected");
4885                 } catch (RemoteException e) {
4886                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
4887                         Log.v(TAG, "Session.onServiceDisconnected: "
4888                                 + "caught RemoteException while responding", e);
4889                     }
4890                 }
4891             }
4892         }
4893 
4894         public abstract void run() throws RemoteException;
4895 
4896         public void onTimedOut() {
4897             IAccountManagerResponse response = getResponseAndClose();
4898             if (response != null) {
4899                 try {
4900                     response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4901                             "timeout");
4902                 } catch (RemoteException e) {
4903                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
4904                         Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4905                                 e);
4906                     }
4907                 }
4908             }
4909         }
4910 
4911         @Override
4912         public void onResult(Bundle result) {
4913             Bundle.setDefusable(result, true);
4914             mNumResults++;
4915             Intent intent = null;
4916             if (result != null) {
4917                 boolean isSuccessfulConfirmCreds = result.getBoolean(
4918                         AccountManager.KEY_BOOLEAN_RESULT, false);
4919                 boolean isSuccessfulUpdateCredsOrAddAccount =
4920                         result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4921                         && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
4922                 // We should only update lastAuthenticated time, if
4923                 // mUpdateLastAuthenticatedTime is true and the confirmRequest
4924                 // or updateRequest was successful
4925                 boolean needUpdate = mUpdateLastAuthenticatedTime
4926                         && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
4927                 if (needUpdate || mAuthDetailsRequired) {
4928                     boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4929                     if (needUpdate && accountPresent) {
4930                         updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4931                     }
4932                     if (mAuthDetailsRequired) {
4933                         long lastAuthenticatedTime = -1;
4934                         if (accountPresent) {
4935                             lastAuthenticatedTime = mAccounts.accountsDb
4936                                     .findAccountLastAuthenticatedTime(
4937                                             new Account(mAccountName, mAccountType));
4938                         }
4939                         result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
4940                                 lastAuthenticatedTime);
4941                     }
4942                 }
4943             }
4944             if (result != null
4945                     && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
4946                 if (!checkKeyIntent(
4947                         Binder.getCallingUid(),
4948                         intent)) {
4949                     onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
4950                             "invalid intent in bundle returned");
4951                     return;
4952                 }
4953             }
4954             if (result != null
4955                     && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
4956                 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4957                 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
4958                 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4959                     Account account = new Account(accountName, accountType);
4960                     cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4961                             new UserHandle(mAccounts.userId));
4962                 }
4963             }
4964             IAccountManagerResponse response;
4965             if (mExpectActivityLaunch && result != null
4966                     && result.containsKey(AccountManager.KEY_INTENT)) {
4967                 response = mResponse;
4968             } else {
4969                 response = getResponseAndClose();
4970             }
4971             if (response != null) {
4972                 try {
4973                     if (result == null) {
4974                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
4975                             Log.v(TAG, getClass().getSimpleName()
4976                                     + " calling onError() on response " + response);
4977                         }
4978                         response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
4979                                 "null bundle returned");
4980                     } else {
4981                         if (mStripAuthTokenFromResult) {
4982                             result.remove(AccountManager.KEY_AUTHTOKEN);
4983                         }
4984                         if (Log.isLoggable(TAG, Log.VERBOSE)) {
4985                             Log.v(TAG, getClass().getSimpleName()
4986                                     + " calling onResult() on response " + response);
4987                         }
4988                         if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4989                                 (intent == null)) {
4990                             // All AccountManager error codes are greater than 0
4991                             response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4992                                     result.getString(AccountManager.KEY_ERROR_MESSAGE));
4993                         } else {
4994                             response.onResult(result);
4995                         }
4996                     }
4997                 } catch (RemoteException e) {
4998                     // if the caller is dead then there is no one to care about remote exceptions
4999                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
5000                         Log.v(TAG, "failure while notifying response", e);
5001                     }
5002                 }
5003             }
5004         }
5005 
5006         @Override
5007         public void onRequestContinued() {
5008             mNumRequestContinued++;
5009         }
5010 
5011         @Override
5012         public void onError(int errorCode, String errorMessage) {
5013             mNumErrors++;
5014             IAccountManagerResponse response = getResponseAndClose();
5015             if (response != null) {
5016                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5017                     Log.v(TAG, getClass().getSimpleName()
5018                             + " calling onError() on response " + response);
5019                 }
5020                 try {
5021                     response.onError(errorCode, errorMessage);
5022                 } catch (RemoteException e) {
5023                     if (Log.isLoggable(TAG, Log.VERBOSE)) {
5024                         Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
5025                     }
5026                 }
5027             } else {
5028                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5029                     Log.v(TAG, "Session.onError: already closed");
5030                 }
5031             }
5032         }
5033 
5034         /**
5035          * find the component name for the authenticator and initiate a bind
5036          * if no authenticator or the bind fails then return false, otherwise return true
5037          */
5038         private boolean bindToAuthenticator(String authenticatorType) {
5039             final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
5040             authenticatorInfo = mAuthenticatorCache.getServiceInfo(
5041                     AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
5042             if (authenticatorInfo == null) {
5043                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5044                     Log.v(TAG, "there is no authenticator for " + authenticatorType
5045                             + ", bailing out");
5046                 }
5047                 return false;
5048             }
5049 
5050             if (!isLocalUnlockedUser(mAccounts.userId)
5051                     && !authenticatorInfo.componentInfo.directBootAware) {
5052                 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
5053                         + " which isn't encryption aware");
5054                 return false;
5055             }
5056 
5057             Intent intent = new Intent();
5058             intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
5059             intent.setComponent(authenticatorInfo.componentName);
5060             if (Log.isLoggable(TAG, Log.VERBOSE)) {
5061                 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
5062             }
5063             int flags = Context.BIND_AUTO_CREATE;
5064             if (mAuthenticatorCache.getBindInstantServiceAllowed(mAccounts.userId)) {
5065                 flags |= Context.BIND_ALLOW_INSTANT;
5066             }
5067             if (!mContext.bindServiceAsUser(intent, this, flags, UserHandle.of(mAccounts.userId))) {
5068                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5069                     Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
5070                 }
5071                 return false;
5072             }
5073 
5074             return true;
5075         }
5076     }
5077 
5078     class MessageHandler extends Handler {
5079         MessageHandler(Looper looper) {
5080             super(looper);
5081         }
5082 
5083         @Override
5084         public void handleMessage(Message msg) {
5085             switch (msg.what) {
5086                 case MESSAGE_TIMED_OUT:
5087                     Session session = (Session)msg.obj;
5088                     session.onTimedOut();
5089                     break;
5090 
5091                 case MESSAGE_COPY_SHARED_ACCOUNT:
5092                     copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
5093                     break;
5094 
5095                 default:
5096                     throw new IllegalStateException("unhandled message: " + msg.what);
5097             }
5098         }
5099     }
5100 
5101     private void logRecord(UserAccounts accounts, String action, String tableName) {
5102         logRecord(action, tableName, -1, accounts);
5103     }
5104 
5105     private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
5106         logRecord(action, tableName, -1, accounts, uid);
5107     }
5108 
5109     /*
5110      * This function receives an opened writable database.
5111      */
5112     private void logRecord(String action, String tableName, long accountId,
5113             UserAccounts userAccount) {
5114         logRecord(action, tableName, accountId, userAccount, getCallingUid());
5115     }
5116 
5117     /*
5118      * This function receives an opened writable database and writes to it in a separate thread.
5119      */
5120     private void logRecord(String action, String tableName, long accountId,
5121             UserAccounts userAccount, int callingUid) {
5122 
5123         class LogRecordTask implements Runnable {
5124             private final String action;
5125             private final String tableName;
5126             private final long accountId;
5127             private final UserAccounts userAccount;
5128             private final int callingUid;
5129             private final long userDebugDbInsertionPoint;
5130 
5131             LogRecordTask(final String action,
5132                     final String tableName,
5133                     final long accountId,
5134                     final UserAccounts userAccount,
5135                     final int callingUid,
5136                     final long userDebugDbInsertionPoint) {
5137                 this.action = action;
5138                 this.tableName = tableName;
5139                 this.accountId = accountId;
5140                 this.userAccount = userAccount;
5141                 this.callingUid = callingUid;
5142                 this.userDebugDbInsertionPoint = userDebugDbInsertionPoint;
5143             }
5144 
5145             @Override
5146             public void run() {
5147                 synchronized (userAccount.accountsDb.mDebugStatementLock) {
5148                     SQLiteStatement logStatement = userAccount.accountsDb.getStatementForLogging();
5149                     if (logStatement == null) {
5150                         return; // Can't log.
5151                     }
5152                     logStatement.bindLong(1, accountId);
5153                     logStatement.bindString(2, action);
5154                     logStatement.bindString(3, mDateFormat.format(new Date()));
5155                     logStatement.bindLong(4, callingUid);
5156                     logStatement.bindString(5, tableName);
5157                     logStatement.bindLong(6, userDebugDbInsertionPoint);
5158                     try {
5159                         logStatement.execute();
5160                     } catch (IllegalStateException e) {
5161                         // Guard against crash, DB can already be closed
5162                         // since this statement is executed on a handler thread
5163                         Slog.w(TAG, "Failed to insert a log record. accountId=" + accountId
5164                                 + " action=" + action + " tableName=" + tableName + " Error: " + e);
5165                     } finally {
5166                         logStatement.clearBindings();
5167                     }
5168                 }
5169             }
5170         }
5171         long insertionPoint = userAccount.accountsDb.reserveDebugDbInsertionPoint();
5172         if (insertionPoint != -1) {
5173             LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
5174                     callingUid, insertionPoint);
5175             mHandler.post(logTask);
5176         }
5177     }
5178 
5179     public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
5180         return asBinder();
5181     }
5182 
5183     /**
5184      * Searches array of arguments for the specified string
5185      * @param args array of argument strings
5186      * @param value value to search for
5187      * @return true if the value is contained in the array
5188      */
5189     private static boolean scanArgs(String[] args, String value) {
5190         if (args != null) {
5191             for (String arg : args) {
5192                 if (value.equals(arg)) {
5193                     return true;
5194                 }
5195             }
5196         }
5197         return false;
5198     }
5199 
5200     @Override
5201     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
5202         if (!DumpUtils.checkDumpPermission(mContext, TAG, fout)) return;
5203         final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
5204         final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, "  ");
5205 
5206         final List<UserInfo> users = getUserManager().getUsers();
5207         for (UserInfo user : users) {
5208             ipw.println("User " + user + ":");
5209             ipw.increaseIndent();
5210             dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
5211             ipw.println();
5212             ipw.decreaseIndent();
5213         }
5214     }
5215 
5216     private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
5217             String[] args, boolean isCheckinRequest) {
5218         if (isCheckinRequest) {
5219             // This is a checkin request. *Only* upload the account types and the count of
5220             // each.
5221             synchronized (userAccounts.dbLock) {
5222                 userAccounts.accountsDb.dumpDeAccountsTable(fout);
5223             }
5224         } else {
5225             Account[] accounts = getAccountsFromCache(userAccounts, null /* type */,
5226                     Process.SYSTEM_UID, null /* packageName */, false);
5227             fout.println("Accounts: " + accounts.length);
5228             for (Account account : accounts) {
5229                 fout.println("  " + account.toString());
5230             }
5231 
5232             // Add debug information.
5233             fout.println();
5234             synchronized (userAccounts.dbLock) {
5235                 userAccounts.accountsDb.dumpDebugTable(fout);
5236             }
5237             fout.println();
5238             synchronized (mSessions) {
5239                 final long now = SystemClock.elapsedRealtime();
5240                 fout.println("Active Sessions: " + mSessions.size());
5241                 for (Session session : mSessions.values()) {
5242                     fout.println("  " + session.toDebugString(now));
5243                 }
5244             }
5245 
5246             fout.println();
5247             mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
5248 
5249             boolean isUserUnlocked;
5250             synchronized (mUsers) {
5251                 isUserUnlocked = isLocalUnlockedUser(userAccounts.userId);
5252             }
5253             // Following logs are printed only when user is unlocked.
5254             if (!isUserUnlocked) {
5255                 return;
5256             }
5257             fout.println();
5258             synchronized (userAccounts.dbLock) {
5259                 Map<Account, Map<String, Integer>> allVisibilityValues =
5260                         userAccounts.accountsDb.findAllVisibilityValues();
5261                 fout.println("Account visibility:");
5262                 for (Account account : allVisibilityValues.keySet()) {
5263                     fout.println("  " + account.name);
5264                     Map<String, Integer> visibilities = allVisibilityValues.get(account);
5265                     for (Entry<String, Integer> entry : visibilities.entrySet()) {
5266                         fout.println("    " + entry.getKey() + ", " + entry.getValue());
5267                     }
5268                 }
5269             }
5270         }
5271     }
5272 
5273     private void doNotification(UserAccounts accounts, Account account, CharSequence message,
5274             Intent intent, String packageName, final int userId) {
5275         long identityToken = clearCallingIdentity();
5276         try {
5277             if (Log.isLoggable(TAG, Log.VERBOSE)) {
5278                 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
5279             }
5280 
5281             if (intent.getComponent() != null &&
5282                     GrantCredentialsPermissionActivity.class.getName().equals(
5283                             intent.getComponent().getClassName())) {
5284                 createNoCredentialsPermissionNotification(account, intent, packageName, userId);
5285             } else {
5286                 Context contextForUser = getContextForUser(new UserHandle(userId));
5287                 final NotificationId id = getSigninRequiredNotificationId(accounts, account);
5288                 intent.addCategory(id.mTag);
5289 
5290                 final String notificationTitleFormat =
5291                         contextForUser.getText(R.string.notification_title).toString();
5292                 Notification n =
5293                         new Notification.Builder(contextForUser, SystemNotificationChannels.ACCOUNT)
5294                         .setWhen(0)
5295                         .setSmallIcon(android.R.drawable.stat_sys_warning)
5296                         .setColor(contextForUser.getColor(
5297                                 com.android.internal.R.color.system_notification_accent_color))
5298                         .setContentTitle(String.format(notificationTitleFormat, account.name))
5299                         .setContentText(message)
5300                         .setContentIntent(PendingIntent.getActivityAsUser(
5301                                 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
5302                                 null, new UserHandle(userId)))
5303                         .build();
5304                 installNotification(id, n, packageName, userId);
5305             }
5306         } finally {
5307             restoreCallingIdentity(identityToken);
5308         }
5309     }
5310 
5311     private void installNotification(NotificationId id, final Notification notification,
5312             String packageName, int userId) {
5313         final long token = clearCallingIdentity();
5314         try {
5315             INotificationManager notificationManager = mInjector.getNotificationManager();
5316             try {
5317                 // The calling uid must match either the package or op package, so use an op
5318                 // package that matches the cleared calling identity.
5319                 notificationManager.enqueueNotificationWithTag(packageName, "android",
5320                         id.mTag, id.mId, notification, userId);
5321             } catch (RemoteException e) {
5322                 /* ignore - local call */
5323             }
5324         } finally {
5325             Binder.restoreCallingIdentity(token);
5326         }
5327     }
5328 
5329     private void cancelNotification(NotificationId id, UserHandle user) {
5330         cancelNotification(id, mContext.getPackageName(), user);
5331     }
5332 
5333     private void cancelNotification(NotificationId id, String packageName, UserHandle user) {
5334         long identityToken = clearCallingIdentity();
5335         try {
5336             INotificationManager service = mInjector.getNotificationManager();
5337             service.cancelNotificationWithTag(packageName, id.mTag, id.mId, user.getIdentifier());
5338         } catch (RemoteException e) {
5339             /* ignore - local call */
5340         } finally {
5341             restoreCallingIdentity(identityToken);
5342         }
5343     }
5344 
5345     private boolean isPermittedForPackage(String packageName, int userId, String... permissions) {
5346         final long identity = Binder.clearCallingIdentity();
5347         try {
5348             final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
5349             IPackageManager pm = ActivityThread.getPackageManager();
5350             for (String perm : permissions) {
5351                 if (pm.checkPermission(perm, packageName, userId)
5352                         == PackageManager.PERMISSION_GRANTED) {
5353                     // Checks runtime permission revocation.
5354                     final int opCode = AppOpsManager.permissionToOpCode(perm);
5355                     if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.checkOpNoThrow(
5356                             opCode, uid, packageName) == AppOpsManager.MODE_ALLOWED) {
5357                         return true;
5358                     }
5359                 }
5360             }
5361         } catch (NameNotFoundException | RemoteException e) {
5362             // Assume permission is not granted if an error accrued.
5363         } finally {
5364             Binder.restoreCallingIdentity(identity);
5365         }
5366         return false;
5367     }
5368 
5369     /**
5370      * Checks that package has at least one of given permissions and makes note of app
5371      * performing the action.
5372      */
5373     private boolean checkPermissionAndNote(String opPackageName, int callingUid,
5374             String... permissions) {
5375         for (String perm : permissions) {
5376             if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
5377                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5378                     Log.v(TAG, "  caller uid " + callingUid + " has " + perm);
5379                 }
5380                 final int opCode = AppOpsManager.permissionToOpCode(perm);
5381                 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
5382                         opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
5383                     return true;
5384                 }
5385             }
5386         }
5387         return false;
5388     }
5389 
5390     private int handleIncomingUser(int userId) {
5391         try {
5392             return ActivityManager.getService().handleIncomingUser(
5393                     Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
5394         } catch (RemoteException re) {
5395             // Shouldn't happen, local.
5396         }
5397         return userId;
5398     }
5399 
5400     private boolean isPrivileged(int callingUid) {
5401         String[] packages;
5402         long identityToken = Binder.clearCallingIdentity();
5403         try {
5404             packages = mPackageManager.getPackagesForUid(callingUid);
5405             if (packages == null) {
5406                 Log.d(TAG, "No packages for callingUid " + callingUid);
5407                 return false;
5408             }
5409             for (String name : packages) {
5410                 try {
5411                     PackageInfo packageInfo =
5412                         mPackageManager.getPackageInfo(name, 0 /* flags */);
5413                     if (packageInfo != null
5414                         && (packageInfo.applicationInfo.privateFlags
5415                             & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
5416                         return true;
5417                     }
5418                 } catch (PackageManager.NameNotFoundException e) {
5419                     Log.d(TAG, "Package not found " + e.getMessage());
5420                 }
5421             }
5422         } finally {
5423             Binder.restoreCallingIdentity(identityToken);
5424         }
5425         return false;
5426     }
5427 
5428     private boolean permissionIsGranted(
5429             Account account, String authTokenType, int callerUid, int userId) {
5430         if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5431             if (Log.isLoggable(TAG, Log.VERBOSE)) {
5432                 Log.v(TAG, "Access to " + account + " granted calling uid is system");
5433             }
5434             return true;
5435         }
5436 
5437         if (isPrivileged(callerUid)) {
5438             if (Log.isLoggable(TAG, Log.VERBOSE)) {
5439                 Log.v(TAG, "Access to " + account + " granted calling uid "
5440                         + callerUid + " privileged");
5441             }
5442             return true;
5443         }
5444         if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
5445             if (Log.isLoggable(TAG, Log.VERBOSE)) {
5446                 Log.v(TAG, "Access to " + account + " granted calling uid "
5447                         + callerUid + " manages the account");
5448             }
5449             return true;
5450         }
5451         if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5452             if (Log.isLoggable(TAG, Log.VERBOSE)) {
5453                 Log.v(TAG, "Access to " + account + " granted calling uid "
5454                         + callerUid + " user granted access");
5455             }
5456             return true;
5457         }
5458 
5459         if (Log.isLoggable(TAG, Log.VERBOSE)) {
5460             Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5461         }
5462 
5463         return false;
5464     }
5465 
5466     private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5467             String opPackageName) {
5468         if (accountType == null) {
5469             return false;
5470         } else {
5471             return getTypesVisibleToCaller(callingUid, userId,
5472                     opPackageName).contains(accountType);
5473         }
5474     }
5475 
5476     // Method checks visibility for applications targeing API level below {@link
5477     // android.os.Build.VERSION_CODES#O},
5478     // returns true if the the app has GET_ACCOUNTS or GET_ACCOUNTS_PRIVILEGED permission.
5479     private boolean checkGetAccountsPermission(String packageName, int userId) {
5480         return isPermittedForPackage(packageName, userId, Manifest.permission.GET_ACCOUNTS,
5481                 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
5482     }
5483 
5484     private boolean checkReadContactsPermission(String packageName, int userId) {
5485         return isPermittedForPackage(packageName, userId, Manifest.permission.READ_CONTACTS);
5486     }
5487 
5488     // Heuristic to check that account type may be associated with some contacts data and
5489     // therefore READ_CONTACTS permission grants the access to account by default.
5490     private boolean accountTypeManagesContacts(String accountType, int userId) {
5491         if (accountType == null) {
5492             return false;
5493         }
5494         long identityToken = Binder.clearCallingIdentity();
5495         Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5496         try {
5497             serviceInfos = mAuthenticatorCache.getAllServices(userId);
5498         } finally {
5499             Binder.restoreCallingIdentity(identityToken);
5500         }
5501         // Check contacts related permissions for authenticator.
5502         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo
5503                 : serviceInfos) {
5504             if (accountType.equals(serviceInfo.type.type)) {
5505                 return isPermittedForPackage(serviceInfo.type.packageName, userId,
5506                     Manifest.permission.WRITE_CONTACTS);
5507             }
5508         }
5509         return false;
5510     }
5511 
5512     /**
5513      * Method checks package uid and signature with Authenticator which manages accountType.
5514      *
5515      * @return SIGNATURE_CHECK_UID_MATCH for uid match, SIGNATURE_CHECK_MATCH for signature match,
5516      *         SIGNATURE_CHECK_MISMATCH otherwise.
5517      */
5518     private int checkPackageSignature(String accountType, int callingUid, int userId) {
5519         if (accountType == null) {
5520             return SIGNATURE_CHECK_MISMATCH;
5521         }
5522 
5523         long identityToken = Binder.clearCallingIdentity();
5524         Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5525         try {
5526             serviceInfos = mAuthenticatorCache.getAllServices(userId);
5527         } finally {
5528             Binder.restoreCallingIdentity(identityToken);
5529         }
5530         // Check for signature match with Authenticator.LocalServices.getService(PackageManagerInternal.class);
5531         PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
5532         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo
5533                 : serviceInfos) {
5534             if (accountType.equals(serviceInfo.type.type)) {
5535                 if (serviceInfo.uid == callingUid) {
5536                     return SIGNATURE_CHECK_UID_MATCH;
5537                 }
5538                 if (pmi.hasSignatureCapability(
5539                         serviceInfo.uid, callingUid,
5540                         PackageParser.SigningDetails.CertCapabilities.AUTH)) {
5541                     return SIGNATURE_CHECK_MATCH;
5542                 }
5543             }
5544         }
5545         return SIGNATURE_CHECK_MISMATCH;
5546     }
5547 
5548     // returns true for applications with the same signature as authenticator.
5549     private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5550         if (accountType == null) {
5551             return false;
5552         } else {
5553             return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5554         }
5555     }
5556 
5557     private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5558             String opPackageName) {
5559         return getTypesForCaller(callingUid, userId, true /* isOtherwisePermitted*/);
5560     }
5561 
5562     private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5563         return getTypesForCaller(callingUid, userId, false);
5564     }
5565 
5566     private List<String> getTypesForCaller(
5567             int callingUid, int userId, boolean isOtherwisePermitted) {
5568         List<String> managedAccountTypes = new ArrayList<>();
5569         long identityToken = Binder.clearCallingIdentity();
5570         Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5571         try {
5572             serviceInfos = mAuthenticatorCache.getAllServices(userId);
5573         } finally {
5574             Binder.restoreCallingIdentity(identityToken);
5575         }
5576 
5577         PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
5578         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
5579                 serviceInfos) {
5580             if (isOtherwisePermitted || pmi.hasSignatureCapability(
5581                     serviceInfo.uid, callingUid,
5582                     PackageParser.SigningDetails.CertCapabilities.AUTH)) {
5583                 managedAccountTypes.add(serviceInfo.type.type);
5584             }
5585         }
5586         return managedAccountTypes;
5587     }
5588 
5589     private boolean isAccountPresentForCaller(String accountName, String accountType) {
5590         if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5591             for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5592                 if (account.name.equals(accountName)) {
5593                     return true;
5594                 }
5595             }
5596         }
5597         return false;
5598     }
5599 
5600     private static void checkManageUsersPermission(String message) {
5601         if (ActivityManager.checkComponentPermission(
5602                 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5603                 != PackageManager.PERMISSION_GRANTED) {
5604             throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5605         }
5606     }
5607 
5608     private static void checkManageOrCreateUsersPermission(String message) {
5609         if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5610                 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5611                 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5612                         Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5613             throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5614                     + message);
5615         }
5616     }
5617 
5618     private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5619             int callerUid) {
5620         if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5621             return true;
5622         }
5623         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
5624         synchronized (accounts.dbLock) {
5625             synchronized (accounts.cacheLock) {
5626                 long grantsCount;
5627                 if (authTokenType != null) {
5628                     grantsCount = accounts.accountsDb
5629                             .findMatchingGrantsCount(callerUid, authTokenType, account);
5630                 } else {
5631                     grantsCount = accounts.accountsDb.findMatchingGrantsCountAnyToken(callerUid,
5632                             account);
5633                 }
5634                 final boolean permissionGranted = grantsCount > 0;
5635 
5636                 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5637                     // TODO: Skip this check when running automated tests. Replace this
5638                     // with a more general solution.
5639                     Log.d(TAG, "no credentials permission for usage of "
5640                             + account.toSafeString() + ", "
5641                             + authTokenType + " by uid " + callerUid
5642                             + " but ignoring since device is in test harness.");
5643                     return true;
5644                 }
5645                 return permissionGranted;
5646             }
5647         }
5648     }
5649 
5650     private boolean isSystemUid(int callingUid) {
5651         String[] packages = null;
5652         long ident = Binder.clearCallingIdentity();
5653         try {
5654             packages = mPackageManager.getPackagesForUid(callingUid);
5655             if (packages != null) {
5656                 for (String name : packages) {
5657                     try {
5658                         PackageInfo packageInfo =
5659                                 mPackageManager.getPackageInfo(name, 0 /* flags */);
5660                         if (packageInfo != null
5661                                 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5662                                 != 0) {
5663                             return true;
5664                         }
5665                     } catch (NameNotFoundException e) {
5666                         Log.w(TAG, String.format("Could not find package [%s]", name), e);
5667                     }
5668                 }
5669             } else {
5670                 Log.w(TAG, "No known packages with uid " + callingUid);
5671             }
5672         } finally {
5673             Binder.restoreCallingIdentity(ident);
5674         }
5675         return false;
5676     }
5677 
5678     /** Succeeds if any of the specified permissions are granted. */
5679     private void checkReadAccountsPermitted(
5680             int callingUid,
5681             String accountType,
5682             int userId,
5683             String opPackageName) {
5684         if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
5685             String msg = String.format(
5686                     "caller uid %s cannot access %s accounts",
5687                     callingUid,
5688                     accountType);
5689             Log.w(TAG, "  " + msg);
5690             throw new SecurityException(msg);
5691         }
5692     }
5693 
5694     private boolean canUserModifyAccounts(int userId, int callingUid) {
5695         // the managing app can always modify accounts
5696         if (isProfileOwner(callingUid)) {
5697             return true;
5698         }
5699         if (getUserManager().getUserRestrictions(new UserHandle(userId))
5700                 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5701             return false;
5702         }
5703         return true;
5704     }
5705 
5706     private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5707         // the managing app can always modify accounts
5708         if (isProfileOwner(callingUid)) {
5709             return true;
5710         }
5711         DevicePolicyManager dpm = (DevicePolicyManager) mContext
5712                 .getSystemService(Context.DEVICE_POLICY_SERVICE);
5713         String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
5714         if (typesArray == null) {
5715             return true;
5716         }
5717         for (String forbiddenType : typesArray) {
5718             if (forbiddenType.equals(accountType)) {
5719                 return false;
5720             }
5721         }
5722         return true;
5723     }
5724 
5725     private boolean isProfileOwner(int uid) {
5726         final DevicePolicyManagerInternal dpmi =
5727                 LocalServices.getService(DevicePolicyManagerInternal.class);
5728         return (dpmi != null)
5729                 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5730     }
5731 
5732     @Override
5733     public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5734             throws RemoteException {
5735         final int callingUid = getCallingUid();
5736 
5737         if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
5738             throw new SecurityException();
5739         }
5740 
5741         if (value) {
5742             grantAppPermission(account, authTokenType, uid);
5743         } else {
5744             revokeAppPermission(account, authTokenType, uid);
5745         }
5746     }
5747 
5748     /**
5749      * Allow callers with the given uid permission to get credentials for account/authTokenType.
5750      * <p>
5751      * Although this is public it can only be accessed via the AccountManagerService object
5752      * which is in the system. This means we don't need to protect it with permissions.
5753      * @hide
5754      */
5755     void grantAppPermission(Account account, String authTokenType, int uid) {
5756         if (account == null || authTokenType == null) {
5757             Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
5758             return;
5759         }
5760         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
5761         synchronized (accounts.dbLock) {
5762             synchronized (accounts.cacheLock) {
5763                 long accountId = accounts.accountsDb.findDeAccountId(account);
5764                 if (accountId >= 0) {
5765                     accounts.accountsDb.insertGrant(accountId, authTokenType, uid);
5766                 }
5767                 cancelNotification(
5768                         getCredentialPermissionNotificationId(account, authTokenType, uid),
5769                         UserHandle.of(accounts.userId));
5770 
5771                 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
5772             }
5773         }
5774 
5775         // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5776         for (AccountManagerInternal.OnAppPermissionChangeListener listener
5777                 : mAppPermissionChangeListeners) {
5778             mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5779         }
5780     }
5781 
5782     /**
5783      * Don't allow callers with the given uid permission to get credentials for
5784      * account/authTokenType.
5785      * <p>
5786      * Although this is public it can only be accessed via the AccountManagerService object
5787      * which is in the system. This means we don't need to protect it with permissions.
5788      * @hide
5789      */
5790     private void revokeAppPermission(Account account, String authTokenType, int uid) {
5791         if (account == null || authTokenType == null) {
5792             Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
5793             return;
5794         }
5795         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
5796         synchronized (accounts.dbLock) {
5797             synchronized (accounts.cacheLock) {
5798                 accounts.accountsDb.beginTransaction();
5799                 try {
5800                     long accountId = accounts.accountsDb.findDeAccountId(account);
5801                     if (accountId >= 0) {
5802                         accounts.accountsDb.deleteGrantsByAccountIdAuthTokenTypeAndUid(
5803                                 accountId, authTokenType, uid);
5804                         accounts.accountsDb.setTransactionSuccessful();
5805                     }
5806                 } finally {
5807                     accounts.accountsDb.endTransaction();
5808                 }
5809 
5810                 cancelNotification(
5811                         getCredentialPermissionNotificationId(account, authTokenType, uid),
5812                         UserHandle.of(accounts.userId));
5813             }
5814         }
5815 
5816         // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5817         for (AccountManagerInternal.OnAppPermissionChangeListener listener
5818                 : mAppPermissionChangeListeners) {
5819             mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5820         }
5821     }
5822 
5823     private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
5824         final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
5825         if (oldAccountsForType != null) {
5826             ArrayList<Account> newAccountsList = new ArrayList<>();
5827             for (Account curAccount : oldAccountsForType) {
5828                 if (!curAccount.equals(account)) {
5829                     newAccountsList.add(curAccount);
5830                 }
5831             }
5832             if (newAccountsList.isEmpty()) {
5833                 accounts.accountCache.remove(account.type);
5834             } else {
5835                 Account[] newAccountsForType = new Account[newAccountsList.size()];
5836                 newAccountsForType = newAccountsList.toArray(newAccountsForType);
5837                 accounts.accountCache.put(account.type, newAccountsForType);
5838             }
5839         }
5840         accounts.userDataCache.remove(account);
5841         accounts.authTokenCache.remove(account);
5842         accounts.previousNameCache.remove(account);
5843         accounts.visibilityCache.remove(account);
5844     }
5845 
5846     /**
5847      * This assumes that the caller has already checked that the account is not already present.
5848      * IMPORTANT: The account being inserted will begin to be tracked for access in remote
5849      * processes and if you will return this account to apps you should return the result.
5850      * @return The inserted account which is a new instance that is being tracked.
5851      */
5852     private Account insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
5853         Account[] accountsForType = accounts.accountCache.get(account.type);
5854         int oldLength = (accountsForType != null) ? accountsForType.length : 0;
5855         Account[] newAccountsForType = new Account[oldLength + 1];
5856         if (accountsForType != null) {
5857             System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
5858         }
5859         String token = account.getAccessId() != null ? account.getAccessId()
5860                 : UUID.randomUUID().toString();
5861         newAccountsForType[oldLength] = new Account(account, token);
5862         accounts.accountCache.put(account.type, newAccountsForType);
5863         return newAccountsForType[oldLength];
5864     }
5865 
5866     @NonNull
5867     private Account[] filterAccounts(UserAccounts accounts, Account[] unfiltered, int callingUid,
5868             @Nullable String callingPackage, boolean includeManagedNotVisible) {
5869         String visibilityFilterPackage = callingPackage;
5870         if (visibilityFilterPackage == null) {
5871             visibilityFilterPackage = getPackageNameForUid(callingUid);
5872         }
5873         Map<Account, Integer> firstPass = new LinkedHashMap<>();
5874         for (Account account : unfiltered) {
5875             int visibility = resolveAccountVisibility(account, visibilityFilterPackage, accounts);
5876             if ((visibility == AccountManager.VISIBILITY_VISIBLE
5877                     || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE)
5878                     || (includeManagedNotVisible
5879                             && (visibility
5880                                     == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE))) {
5881                 firstPass.put(account, visibility);
5882             }
5883         }
5884         Map<Account, Integer> secondPass =
5885                 filterSharedAccounts(accounts, firstPass, callingUid, callingPackage);
5886 
5887         Account[] filtered = new Account[secondPass.size()];
5888         filtered = secondPass.keySet().toArray(filtered);
5889         return filtered;
5890     }
5891 
5892     @NonNull
5893     private Map<Account, Integer> filterSharedAccounts(UserAccounts userAccounts,
5894             @NonNull Map<Account, Integer> unfiltered, int callingUid,
5895             @Nullable String callingPackage) {
5896         // first part is to filter shared accounts.
5897         // unfiltered type check is not necessary.
5898         if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
5899                 || callingUid == Process.SYSTEM_UID) {
5900             return unfiltered;
5901         }
5902         UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
5903         if (user != null && user.isRestricted()) {
5904             String[] packages = mPackageManager.getPackagesForUid(callingUid);
5905             if (packages == null) {
5906                 packages = new String[] {};
5907             }
5908             // If any of the packages is a visible listed package, return the full set,
5909             // otherwise return non-shared accounts only.
5910             // This might be a temporary way to specify a visible list
5911             String visibleList = mContext.getResources().getString(
5912                     com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
5913             for (String packageName : packages) {
5914                 if (visibleList.contains(";" + packageName + ";")) {
5915                     return unfiltered;
5916                 }
5917             }
5918             Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
5919             if (ArrayUtils.isEmpty(sharedAccounts)) {
5920                 return unfiltered;
5921             }
5922             String requiredAccountType = "";
5923             try {
5924                 // If there's an explicit callingPackage specified, check if that package
5925                 // opted in to see restricted accounts.
5926                 if (callingPackage != null) {
5927                     PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
5928                     if (pi != null && pi.restrictedAccountType != null) {
5929                         requiredAccountType = pi.restrictedAccountType;
5930                     }
5931                 } else {
5932                     // Otherwise check if the callingUid has a package that has opted in
5933                     for (String packageName : packages) {
5934                         PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
5935                         if (pi != null && pi.restrictedAccountType != null) {
5936                             requiredAccountType = pi.restrictedAccountType;
5937                             break;
5938                         }
5939                     }
5940                 }
5941             } catch (NameNotFoundException e) {
5942                 Log.d(TAG, "Package not found " + e.getMessage());
5943             }
5944             Map<Account, Integer> filtered = new LinkedHashMap<>();
5945             for (Map.Entry<Account, Integer> entry : unfiltered.entrySet()) {
5946                 Account account = entry.getKey();
5947                 if (account.type.equals(requiredAccountType)) {
5948                     filtered.put(account, entry.getValue());
5949                 } else {
5950                     boolean found = false;
5951                     for (Account shared : sharedAccounts) {
5952                         if (shared.equals(account)) {
5953                             found = true;
5954                             break;
5955                         }
5956                     }
5957                     if (!found) {
5958                         filtered.put(account, entry.getValue());
5959                     }
5960                 }
5961             }
5962             return filtered;
5963         } else {
5964             return unfiltered;
5965         }
5966     }
5967 
5968     /*
5969      * packageName can be null. If not null, it should be used to filter out restricted accounts
5970      * that the package is not allowed to access.
5971      *
5972      * <p>The method shouldn't be called with UserAccounts#cacheLock held, otherwise it will cause a
5973      * deadlock
5974      */
5975     @NonNull
5976     protected Account[] getAccountsFromCache(UserAccounts userAccounts, String accountType,
5977             int callingUid, @Nullable String callingPackage, boolean includeManagedNotVisible) {
5978         Preconditions.checkState(!Thread.holdsLock(userAccounts.cacheLock),
5979                 "Method should not be called with cacheLock");
5980         if (accountType != null) {
5981             Account[] accounts;
5982             synchronized (userAccounts.cacheLock) {
5983                 accounts = userAccounts.accountCache.get(accountType);
5984             }
5985             if (accounts == null) {
5986                 return EMPTY_ACCOUNT_ARRAY;
5987             } else {
5988                 return filterAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
5989                         callingUid, callingPackage, includeManagedNotVisible);
5990             }
5991         } else {
5992             int totalLength = 0;
5993             Account[] accountsArray;
5994             synchronized (userAccounts.cacheLock) {
5995                 for (Account[] accounts : userAccounts.accountCache.values()) {
5996                     totalLength += accounts.length;
5997                 }
5998                 if (totalLength == 0) {
5999                     return EMPTY_ACCOUNT_ARRAY;
6000                 }
6001                 accountsArray = new Account[totalLength];
6002                 totalLength = 0;
6003                 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
6004                     System.arraycopy(accountsOfType, 0, accountsArray, totalLength,
6005                             accountsOfType.length);
6006                     totalLength += accountsOfType.length;
6007                 }
6008             }
6009             return filterAccounts(userAccounts, accountsArray, callingUid, callingPackage,
6010                     includeManagedNotVisible);
6011         }
6012     }
6013 
6014     /** protected by the {@code dbLock}, {@code cacheLock} */
6015     protected void writeUserDataIntoCacheLocked(UserAccounts accounts,
6016             Account account, String key, String value) {
6017         Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
6018         if (userDataForAccount == null) {
6019             userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
6020             accounts.userDataCache.put(account, userDataForAccount);
6021         }
6022         if (value == null) {
6023             userDataForAccount.remove(key);
6024         } else {
6025             userDataForAccount.put(key, value);
6026         }
6027     }
6028 
6029     protected String readCachedTokenInternal(
6030             UserAccounts accounts,
6031             Account account,
6032             String tokenType,
6033             String callingPackage,
6034             byte[] pkgSigDigest) {
6035         synchronized (accounts.cacheLock) {
6036             return accounts.accountTokenCaches.get(
6037                     account, tokenType, callingPackage, pkgSigDigest);
6038         }
6039     }
6040 
6041     /** protected by the {@code dbLock}, {@code cacheLock} */
6042     protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts,
6043             Account account, String key, String value) {
6044         Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
6045         if (authTokensForAccount == null) {
6046             authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
6047             accounts.authTokenCache.put(account, authTokensForAccount);
6048         }
6049         if (value == null) {
6050             authTokensForAccount.remove(key);
6051         } else {
6052             authTokensForAccount.put(key, value);
6053         }
6054     }
6055 
6056     protected String readAuthTokenInternal(UserAccounts accounts, Account account,
6057             String authTokenType) {
6058         // Fast path - check if account is already cached
6059         synchronized (accounts.cacheLock) {
6060             Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
6061             if (authTokensForAccount != null) {
6062                 return authTokensForAccount.get(authTokenType);
6063             }
6064         }
6065         // If not cached yet - do slow path and sync with db if necessary
6066         synchronized (accounts.dbLock) {
6067             synchronized (accounts.cacheLock) {
6068                 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
6069                 if (authTokensForAccount == null) {
6070                     // need to populate the cache for this account
6071                     authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
6072                     accounts.authTokenCache.put(account, authTokensForAccount);
6073                 }
6074                 return authTokensForAccount.get(authTokenType);
6075             }
6076         }
6077     }
6078 
6079     private String readUserDataInternal(UserAccounts accounts, Account account, String key) {
6080         Map<String, String> userDataForAccount;
6081         // Fast path - check if data is already cached
6082         synchronized (accounts.cacheLock) {
6083             userDataForAccount = accounts.userDataCache.get(account);
6084         }
6085         // If not cached yet - do slow path and sync with db if necessary
6086         if (userDataForAccount == null) {
6087             synchronized (accounts.dbLock) {
6088                 synchronized (accounts.cacheLock) {
6089                     userDataForAccount = accounts.userDataCache.get(account);
6090                     if (userDataForAccount == null) {
6091                         // need to populate the cache for this account
6092                         userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
6093                         accounts.userDataCache.put(account, userDataForAccount);
6094                     }
6095                 }
6096             }
6097         }
6098         return userDataForAccount.get(key);
6099     }
6100 
6101     private Context getContextForUser(UserHandle user) {
6102         try {
6103             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
6104         } catch (NameNotFoundException e) {
6105             // Default to mContext, not finding the package system is running as is unlikely.
6106             return mContext;
6107         }
6108     }
6109 
6110     private void sendResponse(IAccountManagerResponse response, Bundle result) {
6111         try {
6112             response.onResult(result);
6113         } catch (RemoteException e) {
6114             // if the caller is dead then there is no one to care about remote
6115             // exceptions
6116             if (Log.isLoggable(TAG, Log.VERBOSE)) {
6117                 Log.v(TAG, "failure while notifying response", e);
6118             }
6119         }
6120     }
6121 
6122     private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
6123             String errorMessage) {
6124         try {
6125             response.onError(errorCode, errorMessage);
6126         } catch (RemoteException e) {
6127             // if the caller is dead then there is no one to care about remote
6128             // exceptions
6129             if (Log.isLoggable(TAG, Log.VERBOSE)) {
6130                 Log.v(TAG, "failure while notifying response", e);
6131             }
6132         }
6133     }
6134 
6135     private final class AccountManagerInternalImpl extends AccountManagerInternal {
6136         private final Object mLock = new Object();
6137 
6138         @GuardedBy("mLock")
6139         private AccountManagerBackupHelper mBackupHelper;
6140 
6141         @Override
6142         public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
6143                 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
6144             if (account == null) {
6145                 Slog.w(TAG, "account cannot be null");
6146                 return;
6147             }
6148             if (packageName == null) {
6149                 Slog.w(TAG, "packageName cannot be null");
6150                 return;
6151             }
6152             if (userId < UserHandle.USER_SYSTEM) {
6153                 Slog.w(TAG, "user id must be concrete");
6154                 return;
6155             }
6156             if (callback == null) {
6157                 Slog.w(TAG, "callback cannot be null");
6158                 return;
6159             }
6160 
6161             int visibility =
6162                 resolveAccountVisibility(account, packageName, getUserAccounts(userId));
6163             if (visibility == AccountManager.VISIBILITY_NOT_VISIBLE) {
6164                 Slog.w(TAG, "requestAccountAccess: account is hidden");
6165                 return;
6166             }
6167 
6168             if (AccountManagerService.this.hasAccountAccess(account, packageName,
6169                     new UserHandle(userId))) {
6170                 Bundle result = new Bundle();
6171                 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
6172                 callback.sendResult(result);
6173                 return;
6174             }
6175 
6176             final int uid;
6177             try {
6178                 long identityToken = clearCallingIdentity();
6179                 try {
6180                     uid = mPackageManager.getPackageUidAsUser(packageName, userId);
6181                 } finally {
6182                     restoreCallingIdentity(identityToken);
6183                 }
6184             } catch (NameNotFoundException e) {
6185                 Slog.e(TAG, "Unknown package " + packageName);
6186                 return;
6187             }
6188 
6189             Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
6190             final UserAccounts userAccounts;
6191             synchronized (mUsers) {
6192                 userAccounts = mUsers.get(userId);
6193             }
6194             SystemNotificationChannels.createAccountChannelForPackage(packageName, uid, mContext);
6195             doNotification(userAccounts, account, null, intent, packageName, userId);
6196         }
6197 
6198         @Override
6199         public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) {
6200             // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
6201             mAppPermissionChangeListeners.add(listener);
6202         }
6203 
6204         @Override
6205         public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) {
6206             return AccountManagerService.this.hasAccountAccess(account, null, uid);
6207         }
6208 
6209         @Override
6210         public byte[] backupAccountAccessPermissions(int userId) {
6211             synchronized (mLock) {
6212                 if (mBackupHelper == null) {
6213                     mBackupHelper = new AccountManagerBackupHelper(
6214                             AccountManagerService.this, this);
6215                 }
6216                 return mBackupHelper.backupAccountAccessPermissions(userId);
6217             }
6218         }
6219 
6220         @Override
6221         public void restoreAccountAccessPermissions(byte[] data, int userId) {
6222             synchronized (mLock) {
6223                 if (mBackupHelper == null) {
6224                     mBackupHelper = new AccountManagerBackupHelper(
6225                             AccountManagerService.this, this);
6226                 }
6227                 mBackupHelper.restoreAccountAccessPermissions(data, userId);
6228             }
6229         }
6230     }
6231 
6232     @VisibleForTesting
6233     static class Injector {
6234         private final Context mContext;
6235 
6236         public Injector(Context context) {
6237             mContext = context;
6238         }
6239 
6240         Looper getMessageHandlerLooper() {
6241             ServiceThread serviceThread = new ServiceThread(TAG,
6242                     android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
6243             serviceThread.start();
6244             return serviceThread.getLooper();
6245         }
6246 
6247         Context getContext() {
6248             return mContext;
6249         }
6250 
6251         void addLocalService(AccountManagerInternal service) {
6252             LocalServices.addService(AccountManagerInternal.class, service);
6253         }
6254 
6255         String getDeDatabaseName(int userId) {
6256             File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
6257                     AccountsDb.DE_DATABASE_NAME);
6258             return databaseFile.getPath();
6259         }
6260 
6261         String getCeDatabaseName(int userId) {
6262             File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
6263                     AccountsDb.CE_DATABASE_NAME);
6264             return databaseFile.getPath();
6265         }
6266 
6267         String getPreNDatabaseName(int userId) {
6268             File systemDir = Environment.getDataSystemDirectory();
6269             File databaseFile = new File(Environment.getUserSystemDirectory(userId),
6270                     PRE_N_DATABASE_NAME);
6271             if (userId == 0) {
6272                 // Migrate old file, if it exists, to the new location.
6273                 // Make sure the new file doesn't already exist. A dummy file could have been
6274                 // accidentally created in the old location,
6275                 // causing the new one to become corrupted as well.
6276                 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
6277                 if (oldFile.exists() && !databaseFile.exists()) {
6278                     // Check for use directory; create if it doesn't exist, else renameTo will fail
6279                     File userDir = Environment.getUserSystemDirectory(userId);
6280                     if (!userDir.exists()) {
6281                         if (!userDir.mkdirs()) {
6282                             throw new IllegalStateException(
6283                                     "User dir cannot be created: " + userDir);
6284                         }
6285                     }
6286                     if (!oldFile.renameTo(databaseFile)) {
6287                         throw new IllegalStateException(
6288                                 "User dir cannot be migrated: " + databaseFile);
6289                     }
6290                 }
6291             }
6292             return databaseFile.getPath();
6293         }
6294 
6295         IAccountAuthenticatorCache getAccountAuthenticatorCache() {
6296             return new AccountAuthenticatorCache(mContext);
6297         }
6298 
6299         INotificationManager getNotificationManager() {
6300             return NotificationManager.getService();
6301         }
6302     }
6303 
6304     private static class NotificationId {
6305         final String mTag;
6306         private final int mId;
6307 
6308         NotificationId(String tag, int type) {
6309             mTag = tag;
6310             mId = type;
6311         }
6312     }
6313 }
6314