blob: 78025764b07ab93ee536a3fdc3e225b1fc772afc [file] [log] [blame]
Fred Quintana60307342009-03-24 22:48:12 -07001/*
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
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.accounts;
Fred Quintana60307342009-03-24 22:48:12 -070018
Doug Zongker885cfc232009-10-21 16:52:44 -070019import android.Manifest;
Carlos Valdivia91979be2015-05-22 14:11:35 -070020import android.accounts.AbstractAccountAuthenticator;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080021import android.accounts.Account;
22import android.accounts.AccountAndUser;
23import android.accounts.AccountAuthenticatorResponse;
24import android.accounts.AccountManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070025import android.accounts.AccountManagerInternal;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080026import android.accounts.AuthenticatorDescription;
Amith Yamasani23c8b962013-04-10 13:37:18 -070027import android.accounts.CantAddAccountActivity;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080028import android.accounts.GrantCredentialsPermissionActivity;
29import android.accounts.IAccountAuthenticator;
30import android.accounts.IAccountAuthenticatorResponse;
31import android.accounts.IAccountManager;
32import android.accounts.IAccountManagerResponse;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070033import android.annotation.IntRange;
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -070034import android.annotation.NonNull;
Svet Ganovf6d424f12016-09-20 20:18:53 -070035import android.annotation.Nullable;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080036import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070037import android.app.ActivityManagerNative;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070038import android.app.ActivityThread;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070039import android.app.AppGlobals;
Svetoslavf3f02ac2015-09-08 14:36:35 -070040import android.app.AppOpsManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070041import android.app.INotificationManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070042import android.app.Notification;
43import android.app.NotificationManager;
44import android.app.PendingIntent;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000045import android.app.admin.DeviceAdminInfo;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010046import android.app.admin.DevicePolicyManager;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000047import android.app.admin.DevicePolicyManagerInternal;
Fred Quintanaa698f422009-04-08 19:14:54 -070048import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070049import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070050import android.content.Context;
51import android.content.Intent;
52import android.content.IntentFilter;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070053import android.content.IntentSender;
Fred Quintanab839afc2009-10-14 15:57:28 -070054import android.content.ServiceConnection;
Carlos Valdivia6ede9c32016-03-10 20:12:32 -080055import android.content.pm.ActivityInfo;
Doug Zongker885cfc232009-10-21 16:52:44 -070056import android.content.pm.ApplicationInfo;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070057import android.content.pm.IPackageManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070058import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070059import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070060import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070061import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070062import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070063import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070064import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070065import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070066import android.database.Cursor;
Fred Quintanaa698f422009-04-08 19:14:54 -070067import android.database.sqlite.SQLiteDatabase;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070068import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070069import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070070import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080071import android.os.Environment;
Fred Quintanaa698f422009-04-08 19:14:54 -070072import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070073import android.os.IBinder;
74import android.os.Looper;
75import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070076import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070077import android.os.Process;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070078import android.os.RemoteCallback;
Fred Quintanaa698f422009-04-08 19:14:54 -070079import android.os.RemoteException;
80import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070081import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070082import android.os.UserManager;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070083import android.os.storage.StorageManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070084import android.text.TextUtils;
85import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070086import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070087import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080088import android.util.SparseArray;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070089import android.util.SparseBooleanArray;
Fred Quintana60307342009-03-24 22:48:12 -070090
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070091import com.android.internal.R;
Svet Ganov5d09c992016-09-07 09:57:41 -070092import com.android.internal.annotations.GuardedBy;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -070093import com.android.internal.annotations.VisibleForTesting;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070094import com.android.internal.content.PackageMonitor;
Amith Yamasani67df64b2012-12-14 12:09:36 -080095import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -080096import com.android.internal.util.IndentingPrintWriter;
Fyodor Kupolov35f68082016-04-06 12:14:17 -070097import com.android.internal.util.Preconditions;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000098import com.android.server.LocalServices;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -070099import com.android.server.ServiceThread;
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600100import com.android.server.SystemService;
101
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700102import com.google.android.collect.Lists;
103import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700104
Oscar Montemayora8529f62009-11-18 10:14:20 -0800105import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -0700106import java.io.FileDescriptor;
107import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -0800108import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -0700109import java.security.MessageDigest;
110import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700111import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -0700112import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -0800113import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700114import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700115import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700116import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700117import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -0800118import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700119import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800120import java.util.Map;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800121import java.util.Map.Entry;
Svet Ganovc1c0d1c2016-09-23 19:15:47 -0700122import java.util.Objects;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700123import java.util.Set;
Svet Ganovc1c0d1c2016-09-23 19:15:47 -0700124import java.util.UUID;
Svet Ganovf6d424f12016-09-20 20:18:53 -0700125import java.util.concurrent.CopyOnWriteArrayList;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700126import java.util.concurrent.atomic.AtomicInteger;
127import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700128
Fred Quintana60307342009-03-24 22:48:12 -0700129/**
130 * A system service that provides account, password, and authtoken management for all
131 * accounts on the device. Some of these calls are implemented with the help of the corresponding
132 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
133 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700134 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700135 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700136 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700137public class AccountManagerService
138 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800139 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -0700140 private static final String TAG = "AccountManagerService";
141
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600142 public static class Lifecycle extends SystemService {
143 private AccountManagerService mService;
144
145 public Lifecycle(Context context) {
146 super(context);
147 }
148
149 @Override
150 public void onStart() {
Fyodor Kupolovda993802016-09-21 14:47:10 -0700151 mService = new AccountManagerService(new Injector(getContext()));
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600152 publishBinderService(Context.ACCOUNT_SERVICE, mService);
153 }
154
155 @Override
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600156 public void onUnlockUser(int userHandle) {
157 mService.onUnlockUser(userHandle);
158 }
159 }
160
Svet Ganov5d09c992016-09-07 09:57:41 -0700161 final Context mContext;
Fred Quintana60307342009-03-24 22:48:12 -0700162
Fred Quintana56285a62010-12-02 14:20:51 -0800163 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700164 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700165 private UserManager mUserManager;
Fyodor Kupolovda993802016-09-21 14:47:10 -0700166 private final Injector mInjector;
Fred Quintana56285a62010-12-02 14:20:51 -0800167
Svet Ganov5d09c992016-09-07 09:57:41 -0700168 final MessageHandler mHandler;
Tejas Khorana7b88f0e2016-06-13 13:06:35 -0700169
Fred Quintana60307342009-03-24 22:48:12 -0700170 // Messages that can be sent on mHandler
171 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700172 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700173
Fred Quintana56285a62010-12-02 14:20:51 -0800174 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700175 private static final String PRE_N_DATABASE_NAME = "accounts.db";
Fred Quintana7be59642009-08-24 18:29:25 -0700176 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800177
Carlos Valdivia91979be2015-05-22 14:11:35 -0700178 static {
179 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
180 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
181 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700182
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800183
Fred Quintanaa698f422009-04-08 19:14:54 -0700184 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700185 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
186
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700187 private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
188
Amith Yamasani04e0d262012-02-14 11:50:53 -0800189 static class UserAccounts {
190 private final int userId;
Fyodor Kupolov00de49e2016-09-23 13:10:27 -0700191 final AccountsDb accountsDb;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800192 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
193 credentialsPermissionNotificationIds =
194 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
195 private final HashMap<Account, Integer> signinRequiredNotificationIds =
196 new HashMap<Account, Integer>();
Svet Ganov5d09c992016-09-07 09:57:41 -0700197 final Object cacheLock = new Object();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800198 /** protected by the {@link #cacheLock} */
Svet Ganov5d09c992016-09-07 09:57:41 -0700199 final HashMap<String, Account[]> accountCache =
Svet Ganovf6d424f12016-09-20 20:18:53 -0700200 new LinkedHashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800201 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700202 private final Map<Account, Map<String, String>> userDataCache = new HashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800203 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700204 private final Map<Account, Map<String, String>> authTokenCache = new HashMap<>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700205
206 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700207 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700208
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700209 /** protected by the {@link #cacheLock} */
210 private final Map<String, ArrayList<Integer>> mApplicationAccountRequestMappings =
211 new HashMap<>();
212
213 /* Together the below two Sparse Arrays serve as visible list. One maps UID to account
214 number. Another maps Account number to Account.*/
215
216 /** protected by the {@link #cacheLock} */
217 private final SparseArray<ArrayList<Integer>> mVisibleListUidToMockAccountNumbers =
218 new SparseArray<>();
219
220 //TODO: Instead of using Mock Account IDs, use the actual account IDs.
221 /** protected by the {@link #cacheLock} */
222 private final SparseArray<Account> mMockAccountIdToAccount = new SparseArray<>();
223
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700224 /**
225 * protected by the {@link #cacheLock}
226 *
227 * Caches the previous names associated with an account. Previous names
228 * should be cached because we expect that when an Account is renamed,
229 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
230 * want to know if the accounts they care about have been renamed.
231 *
232 * The previous names are wrapped in an {@link AtomicReference} so that
233 * we can distinguish between those accounts with no previous names and
234 * those whose previous names haven't been cached (yet).
235 */
236 private final HashMap<Account, AtomicReference<String>> previousNameCache =
237 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800238
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700239 private int debugDbInsertionPoint = -1;
Fyodor Kupolov00de49e2016-09-23 13:10:27 -0700240 private SQLiteStatement statementForLogging; // TODO Move to AccountsDb
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700241
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700242 UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800243 this.userId = userId;
244 synchronized (cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -0700245 accountsDb = AccountsDb.create(context, userId, preNDbFile, deDbFile);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800246 }
247 }
248 }
249
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700250 private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600251 private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -0700252 // Not thread-safe. Only use in synchronized context
253 private final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Svet Ganovf6d424f12016-09-20 20:18:53 -0700254 private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
255 mAppPermissionChangeListeners = new CopyOnWriteArrayList<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800256
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700257 private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
Fred Quintana31957f12009-10-21 13:43:10 -0700258 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700259
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700260 /**
261 * This should only be called by system code. One should only call this after the service
262 * has started.
263 * @return a reference to the AccountManagerService instance
264 * @hide
265 */
266 public static AccountManagerService getSingleton() {
267 return sThis.get();
268 }
Fred Quintana60307342009-03-24 22:48:12 -0700269
Fyodor Kupolovda993802016-09-21 14:47:10 -0700270 public AccountManagerService(Injector injector) {
271 mInjector = injector;
272 mContext = injector.getContext();
273 mPackageManager = mContext.getPackageManager();
Svetoslavf3f02ac2015-09-08 14:36:35 -0700274 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fyodor Kupolovda993802016-09-21 14:47:10 -0700275 mHandler = new MessageHandler(injector.getMessageHandlerLooper());
276 mAuthenticatorCache = mInjector.getAccountAuthenticatorCache();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800277 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700278
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700279 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800280
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700281 addRequestsForPreInstalledApplications();
282
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800283 IntentFilter intentFilter = new IntentFilter();
284 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
285 intentFilter.addDataScheme("package");
286 mContext.registerReceiver(new BroadcastReceiver() {
287 @Override
288 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700289 // Don't delete accounts when updating a authenticator's
290 // package.
291 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700292 /* Purging data requires file io, don't block the main thread. This is probably
293 * less than ideal because we are introducing a race condition where old grants
294 * could be exercised until they are purged. But that race condition existed
295 * anyway with the broadcast receiver.
296 *
297 * Ideally, we would completely clear the cache, purge data from the database,
298 * and then rebuild the cache. All under the cache lock. But that change is too
299 * large at this point.
300 */
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700301 Runnable purgingRunnable = new Runnable() {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700302 @Override
303 public void run() {
304 purgeOldGrantsAll();
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700305
306 /* clears application request's for account types supported */
307 int uidOfUninstalledApplication =
308 intent.getIntExtra(Intent.EXTRA_UID, -1);
309 if(uidOfUninstalledApplication != -1) {
310 clearRequestedAccountVisibility(uidOfUninstalledApplication,
311 getUserAccounts(UserHandle.getUserId(
312 uidOfUninstalledApplication)));
313 }
314
315 /* removes visibility of previous UID of this uninstalled application*/
316 removeAccountVisibilityAllAccounts(uidOfUninstalledApplication,
317 getUserAccounts(UserHandle.getUserId(
318 uidOfUninstalledApplication)));
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700319 }
320 };
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700321 mHandler.post(purgingRunnable);
Carlos Valdivia23f58262014-09-05 10:52:41 -0700322 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700323
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800324 }
325 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800326
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700327 IntentFilter packageAddedOrChangedFilter = new IntentFilter();
328 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
329 packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
330 packageAddedOrChangedFilter.addDataScheme("package");
331 mContext.registerReceiverAsUser(new BroadcastReceiver() {
332 @Override
333 public void onReceive(Context context1, Intent intent) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700334 mHandler.post(new Runnable() {
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700335 @Override
336 public void run() {
337 int uidOfInstalledApplication =
338 intent.getIntExtra(Intent.EXTRA_UID, -1);
339 if(uidOfInstalledApplication != -1) {
340 registerAccountTypesSupported(
341 uidOfInstalledApplication,
342 getUserAccounts(
343 UserHandle.getUserId(uidOfInstalledApplication)));
344 }
345 }
346 });
347 }
348 }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
349
Amith Yamasani13593602012-03-22 16:16:17 -0700350 IntentFilter userFilter = new IntentFilter();
351 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800352 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700353 @Override
354 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800355 String action = intent.getAction();
356 if (Intent.ACTION_USER_REMOVED.equals(action)) {
357 onUserRemoved(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800358 }
Amith Yamasani13593602012-03-22 16:16:17 -0700359 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800360 }, UserHandle.ALL, userFilter, null, null);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700361
Fyodor Kupolovda993802016-09-21 14:47:10 -0700362 injector.addLocalService(new AccountManagerInternalImpl());
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700363
364 // Need to cancel account request notifications if the update/install can access the account
365 new PackageMonitor() {
366 @Override
367 public void onPackageAdded(String packageName, int uid) {
368 // Called on a handler, and running as the system
369 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
370 }
371
372 @Override
373 public void onPackageUpdateFinished(String packageName, int uid) {
374 // Called on a handler, and running as the system
375 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
376 }
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700377 }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700378
379 // Cancel account request notification if an app op was preventing the account access
380 mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
381 new AppOpsManager.OnOpChangedInternalListener() {
382 @Override
383 public void onOpChanged(int op, String packageName) {
384 try {
385 final int userId = ActivityManager.getCurrentUser();
386 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
387 final int mode = mAppOpsManager.checkOpNoThrow(
388 AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
389 if (mode == AppOpsManager.MODE_ALLOWED) {
390 final long identity = Binder.clearCallingIdentity();
391 try {
392 cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
393 } finally {
394 Binder.restoreCallingIdentity(identity);
395 }
396 }
397 } catch (NameNotFoundException e) {
398 /* ignore */
399 }
400 }
401 });
402
403 // Cancel account request notification if a permission was preventing the account access
404 mPackageManager.addOnPermissionsChangeListener(
405 (int uid) -> {
406 Account[] accounts = null;
407 String[] packageNames = mPackageManager.getPackagesForUid(uid);
408 if (packageNames != null) {
409 final int userId = UserHandle.getUserId(uid);
410 final long identity = Binder.clearCallingIdentity();
411 try {
412 for (String packageName : packageNames) {
Fyodor Kupolovda993802016-09-21 14:47:10 -0700413 if (mPackageManager.checkPermission(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700414 Manifest.permission.GET_ACCOUNTS, packageName)
415 != PackageManager.PERMISSION_GRANTED) {
416 continue;
417 }
418
419 if (accounts == null) {
420 accounts = getAccountsAsUser(null, userId, "android");
421 if (ArrayUtils.isEmpty(accounts)) {
422 return;
423 }
424 }
425
426 for (Account account : accounts) {
427 cancelAccountAccessRequestNotificationIfNeeded(
428 account, uid, packageName, true);
429 }
430 }
431 } finally {
432 Binder.restoreCallingIdentity(identity);
433 }
434 }
435 });
436 }
437
438 private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
439 boolean checkAccess) {
440 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
441 for (Account account : accounts) {
442 cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
443 }
444 }
445
446 private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
447 boolean checkAccess) {
448 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
449 for (Account account : accounts) {
450 cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
451 }
452 }
453
454 private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
455 boolean checkAccess) {
456 String[] packageNames = mPackageManager.getPackagesForUid(uid);
457 if (packageNames != null) {
458 for (String packageName : packageNames) {
459 cancelAccountAccessRequestNotificationIfNeeded(account, uid,
460 packageName, checkAccess);
461 }
462 }
463 }
464
465 private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
466 int uid, String packageName, boolean checkAccess) {
467 if (!checkAccess || hasAccountAccess(account, packageName,
468 UserHandle.getUserHandleForUid(uid))) {
469 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -0700470 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700471 UserHandle.getUserHandleForUid(uid));
472 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800473 }
474
Dianne Hackborn164371f2013-10-01 19:10:13 -0700475 @Override
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700476 public boolean addAccountExplicitlyWithUid(Account account, String password, Bundle extras,
477 int[] selectedUids) {
478 if(addAccountExplicitly(account,password,extras)) {
479 for(int thisUid : selectedUids) {
480 makeAccountVisible(account, thisUid);
481 }
482 return true;
483 }
484 return false;
485 }
486
487 @Override
488 public int[] getRequestingUidsForType(String accountType) {
489 int callingUid = Binder.getCallingUid();
490 if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
491 String msg = String.format(
492 "uid %s cannot get secrets for accounts of type: %s",
493 callingUid,
494 accountType);
495 throw new SecurityException(msg);
496 }
497 return getRequestingUidsForType(accountType, getUserAccounts(
498 UserHandle.getUserId(callingUid)));
499 }
500
501 /**
502 * Returns all UIDs for applications that requested the account type. This method
503 * is called indirectly by the Authenticator and AccountManager
504 *
505 * @param accountType authenticator would like to know the requesting apps of
506 * @param ua UserAccount that currently hosts the account and application
507 *
508 * @return ArrayList of all UIDs that support accounts of this
509 * account type that seek approval (to be used to know which accounts for
510 * the authenticator to include in addAccountExplicitly). Null if none.
511 */
512 private int[] getRequestingUidsForType(String accountType, UserAccounts ua) {
513 synchronized(ua.cacheLock) {
514 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
515 ua.mApplicationAccountRequestMappings;
516 ArrayList<Integer> allUidsForAccountType = userApplicationAccountRequestMappings.get(
517 accountType);
518 if(allUidsForAccountType == null) {
519 return null;
520 }
521 int[] toReturn = new int[allUidsForAccountType.size()];
522 for(int i = 0 ; i < toReturn.length ; i++) {
523 toReturn[i] = allUidsForAccountType.get(i);
524 }
525 return toReturn;
526 }
527 }
528
529 @Override
530 public boolean isAccountVisible(Account a, int uid) {
531 int callingUid = Binder.getCallingUid();
532 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
533 String msg = String.format(
534 "uid %s cannot get secrets for accounts of type: %s",
535 callingUid,
536 a.type);
537 throw new SecurityException(msg);
538 }
539 return isAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
540 }
541
542 /**
543 * Checks visibility of certain account of a process identified
544 * by a given UID. This is called by the Authenticator indirectly.
545 *
546 * @param a The account to check visibility of
547 * @param uid UID to check visibility of
548 * @param ua UserAccount that currently hosts the account and application
549 *
550 * @return True if application has access to the account
551 *
552 */
553 private boolean isAccountVisible(Account a, int uid, UserAccounts ua) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700554 if(isAccountManagedByCaller(a.type, uid, UserHandle.getUserId(uid))) {
555 return true;
556 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700557 int accountMapping = getMockAccountNumber(a, ua);
558 if(accountMapping < 0) {
559 return true;
560 }
561 synchronized(ua.cacheLock) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700562 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700563 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
564 ua.mVisibleListUidToMockAccountNumbers;
565 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
Tejas Khorana69990d92016-08-03 11:19:40 -0700566 int indexOfAccountMapping = userAcctIdToAcctMap.indexOfValueByValue(a);
567 return indexOfAccountMapping == -1 || (linkedAccountsToUid != null
568 && linkedAccountsToUid.contains(accountMapping));
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700569 }
570 }
571
572 @Override
573 public boolean makeAccountVisible(Account a, int uid) {
574 int callingUid = Binder.getCallingUid();
575 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
576 String msg = String.format(
577 "uid %s cannot get secrets for accounts of type: %s",
578 callingUid,
579 a.type);
580 throw new SecurityException(msg);
581 }
582 return makeAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
583 }
584
585 /**
586 * Gives a certain UID, represented a application, access to an account. This method
587 * is called indirectly by the Authenticator.
588 *
589 * @param a Account to make visible
590 * @param uid to add visibility of the Account from
591 * @param ua UserAccount that currently hosts the account and application
592 *
593 * @return True if account made visible to application and was not previously visible.
594 */
595 private boolean makeAccountVisible(Account a, int uid, UserAccounts ua) {
596 int accountMapping = getMockAccountNumber(a, ua);
597 if(accountMapping < 0) {
598 accountMapping = makeAccountNumber(a, ua);
599 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700600 synchronized(ua.cacheLock) {
601 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
602 ua.mVisibleListUidToMockAccountNumbers;
603 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
604 if(linkedAccountsToUid == null) {
605 linkedAccountsToUid = new ArrayList<>();
606 linkedAccountsToUid.add(accountMapping);
607 userWlUidToMockAccountNums.put(uid, linkedAccountsToUid);
608 } else if(!linkedAccountsToUid.contains(accountMapping)) {
609 linkedAccountsToUid.add(accountMapping);
610 } else {
611 return false;
612 }
613 }
614
615 String[] subPackages = mPackageManager.getPackagesForUid(uid);
616 if(subPackages != null) {
617 for(String subPackage : subPackages) {
618 sendNotification(subPackage, a);
619 }
620 }
621 return true;
622 }
623
624 @Override
625 public boolean removeAccountVisibility(Account a, int uid) {
626 int callingUid = Binder.getCallingUid();
627 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
628 String msg = String.format(
629 "uid %s cannot get secrets for accounts of type: %s",
630 callingUid,
631 a.type);
632 throw new SecurityException(msg);
633 }
634 return removeAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
635 }
636
637 /**
638 * Removes visibility of certain account of a process identified
639 * by a given UID to an application. This is called directly by the
640 * AccountManager and indirectly by the Authenticator.
641 *
642 * @param a Account to remove visibility from
643 * @param uid UID to remove visibility of the Account from
644 * @param ua UserAccount that hosts the account and application
645 *
646 * @return True if application access to account removed and was previously visible.
647 */
648 private boolean removeAccountVisibility(Account a, int uid, UserAccounts ua) {
649 int accountMapping = getMockAccountNumber(a, ua);
650 if(accountMapping < 0) {
651 return false;
652 }
653 synchronized(ua.cacheLock) {
654 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
655 ua.mVisibleListUidToMockAccountNumbers;
656 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
657 if(linkedAccountsToUid != null) {
658 boolean toReturn = linkedAccountsToUid.remove((Integer) accountMapping);
659 if(linkedAccountsToUid.size() == 0) {
660 userWlUidToMockAccountNums.remove(uid);
661 }
662 return toReturn;
663 }
664 }
665 return false;
666 }
667
668 /**
669 * Registers an application's preferences for supported account types for login. This is
670 * a helper method of requestAccountVisibility and indirectly called by AccountManager.
671 *
672 * @param accountTypes account types third party app is willing to support
673 * @param uid of application requesting account visibility
674 * @param ua UserAccount that hosts the account and application
675 */
676 private void addRequestedAccountsVisibility(String[] accountTypes, int uid, UserAccounts ua) {
677 synchronized(ua.cacheLock) {
678 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
679 ua.mApplicationAccountRequestMappings;
680 for(String accountType : accountTypes) {
681 ArrayList<Integer> accountUidAppList = userApplicationAccountRequestMappings
682 .get(accountType);
683 if(accountUidAppList == null) {
684 accountUidAppList = new ArrayList<>();
685 accountUidAppList.add(uid);
686 userApplicationAccountRequestMappings.put(accountType, accountUidAppList);
687 } else if (!accountUidAppList.contains(uid)) {
688 accountUidAppList.add(uid);
689 }
690 }
691 }
692 }
693
694 /**
695 * Registers the requested login account types requested by all the applications already
696 * installed on the device.
697 */
698 private void addRequestsForPreInstalledApplications() {
Fyodor Kupolovda993802016-09-21 14:47:10 -0700699 List<PackageInfo> allInstalledPackages = mPackageManager.getInstalledPackages(0);
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700700 for(PackageInfo pi : allInstalledPackages) {
701 int currentUid = pi.applicationInfo.uid;
702 if(currentUid != -1) {
703 registerAccountTypesSupported(currentUid,
704 getUserAccounts(UserHandle.getUserId(currentUid)));
705 }
706 }
707 }
708
709 /**
710 * Clears all preferences an application had for login account types it offered
711 * support for. This method is used by AccountManager after application is
712 * uninstalled.
713 *
714 * @param uid Uid of the application to clear account type preferences
715 * @param ua UserAccount that hosted the account and application
716 *
717 * @return true if any previous settings were overridden.
718 */
719 private boolean clearRequestedAccountVisibility(int uid, UserAccounts ua) {
720 boolean accountsDeleted = false;
721 ArrayList<String> accountTypesToRemove = new ArrayList<>();
722 synchronized(ua.cacheLock) {
723 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
724 ua.mApplicationAccountRequestMappings;
725 Set<Entry<String, ArrayList<Integer>>> accountTypeAppListEntries =
726 userApplicationAccountRequestMappings.entrySet();
727
728 for(Entry<String, ArrayList<Integer>> entry : accountTypeAppListEntries) {
729 ArrayList<Integer> supportedApps = entry.getValue();
730 if(supportedApps.remove((Integer) uid)) {
731 accountsDeleted = true;
732 }
733
734 if(supportedApps.isEmpty()) {
735 accountTypesToRemove.add(entry.getKey());
736 }
737 }
738
739 for(String s : accountTypesToRemove) {
740 userApplicationAccountRequestMappings.remove(s);
741 }
742 }
743
744 return accountsDeleted;
745 }
746
747 /**
748 * Retrieves the mock account number associated with an Account in order to later retrieve
749 * the account from the Integer-Account Mapping. An account number is not the same as
750 * accountId in the database. This method can be indirectly called by AccountManager and
751 * indirectly by the Authenticator.
752 *
753 * @param a account to retrieve account number mapping
754 * @param ua UserAccount that currently hosts the account and application
755 *
756 * @return account number affiliated with the Account in question. Negative number if none.
757 */
758 private int getMockAccountNumber(Account a, UserAccounts ua) {
759 //TODO: Each account is linked to AccountId rather than generated mock account numbers
760 SparseArray<Account> userAcctIdToAcctMap =
761 ua.mMockAccountIdToAccount;
762 synchronized(ua.cacheLock) {
763 int indexOfAccount = userAcctIdToAcctMap.indexOfValueByValue(a);
764 if(indexOfAccount < 0) {
765 return -1;
766 }
767 return userAcctIdToAcctMap.keyAt(indexOfAccount);
768 }
769 }
770
771 /**
772 * Returns a full list of accounts that a certain UID is allowed access
773 * based on the visible list entries.
774 *
775 * @param uid of application to retrieve visible listed accounts for
776 * @param ua UserAccount that currently hosts the account and application
777 *
778 * @return array of Account values that are accessible by the given uids
779 */
780 private Account[] getVisibleListedAccounts(int uid, UserAccounts ua) {
781 ArrayList<Account> visibleListedAccounts = new ArrayList<>();
782 synchronized(ua.cacheLock) {
783 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
784 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
785 ua.mVisibleListUidToMockAccountNumbers;
786 ArrayList<Integer> visibleListedUidAccountNumbers =
787 userWlUidToMockAccountNums.get(uid);
788 if(visibleListedUidAccountNumbers != null) {
789 for(Integer accountNumber : visibleListedUidAccountNumbers) {
790 Account currentAccount = userAcctIdToAcctMap.get(accountNumber);
791 visibleListedAccounts.add(currentAccount);
792 }
793 }
794 }
795 Account[] arrVisibleListedAccounts = new Account[visibleListedAccounts.size()];
796 return visibleListedAccounts.toArray(arrVisibleListedAccounts);
797 }
798
799 /**
800 * Makes an account number for a given Account to be mapped to.
801 * This method is called by makeVisible if an Account does not have
802 * a mapping for the visible list. This method is thus indirectly
803 * called by the Authenticator.
804 *
805 * @param a account to make an account number mapping of
806 * @param ua UserAccount that currently hosts the account and application
807 *
808 * @return account number created to map to the given account
809 */
810 // TODO: Remove this method and use accountId from DB.
811 private int makeAccountNumber(Account a, UserAccounts ua) {
812 synchronized(ua.cacheLock) {
813 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
814 int newAccountMapping = 0;
815 while(userAcctIdToAcctMap.get(newAccountMapping) != null) {
816 newAccountMapping++;
817 }
818 userAcctIdToAcctMap.put(newAccountMapping, a);
819 return newAccountMapping;
820 }
821 }
822
823
824
825 /**
826 * Registers an application, represented by a UID, to support account types detailed in
827 * the applications manifest as well as allowing it to opt for notifications.
828 *
829 * @param uid UID of application
830 * @param ua UserAccount that currently hosts the account and application
831 */
832 private void registerAccountTypesSupported(int uid, UserAccounts ua) {
833 /* Account types supported are drawn from the Android Manifest of the Application */
834 String interestedPackages = null;
835 try {
836 String[] allPackages = mPackageManager.getPackagesForUid(uid);
Nicolas Prevotf7d8df12016-09-16 17:45:34 +0100837 if (allPackages != null) {
838 for(String aPackage : allPackages) {
839 ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
840 PackageManager.GET_META_DATA);
841 Bundle b = ai.metaData;
842 if(b == null) {
843 return;
844 }
845 interestedPackages = b.getString("android.accounts.SupportedLoginTypes");
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700846 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700847 }
848 } catch (PackageManager.NameNotFoundException e) {
849 Log.d("NameNotFoundException", e.getMessage());
850 }
851 if(interestedPackages != null) {
852 /* request remote account types directly from here. Reads from Android Manifest */
853 requestAccountVisibility(interestedPackages.split(";"), uid, ua);
854 }
855 }
856
857 /**
858 * Allows AccountManager to register account types that an application has login
859 * support for. This method over-writes all of the application's previous settings
860 * for accounts it supported.
861 *
862 * @param accountTypes array of account types application wishes to support
863 * @param uid of application registering requested account types
864 * @param ua UserAccount that hosts the account and application
865 */
866 private void requestAccountVisibility(String[] accountTypes, int uid, UserAccounts ua) {
867 if(accountTypes.length > 0) {
868 clearRequestedAccountVisibility(uid, ua);
869 addRequestedAccountsVisibility(accountTypes, uid, ua);
870 }
871 }
872
873 /**
874 * Removes visibility of all Accounts to this particular UID. This is called when an
875 * application is uninstalled so another application that is installed with the same
876 * UID cannot access Accounts. This is called by AccountManager.
877 *
878 * @param uid of application to remove all Account visibility to
879 * @param ua UserAccount that hosts the current Account
880 */
881 private void removeAccountVisibilityAllAccounts(int uid, UserAccounts ua) {
882 synchronized(ua.cacheLock) {
883 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
884 ua.mVisibleListUidToMockAccountNumbers;
885 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
886 ArrayList<Integer> allAccountNumbersList = userWlUidToMockAccountNums.get(uid);
887 if(allAccountNumbersList != null) {
888 Integer[] allAccountNumbers = allAccountNumbersList.toArray(
889 new Integer[allAccountNumbersList.size()]);
890 for(int accountNum : allAccountNumbers) {
891 removeAccountVisibility(userAcctIdToAcctMap.get(accountNum), uid, ua);
892 }
893 }
894 }
895 }
896
897 /**
898 * Removes visible list functionality of a certain Account.
899 * This method is currently called by (1) addAccountExplicitly (as opposed to
900 * addAccountExplicitlyWithUid) and (2) removeAccountExplicitly.
901 *
902 * @param a the account to clear the visible list functionality for
903 * @param ua currently UserAccounts profile containing Account
904 *
905 * @return true if account previously had visible list functionality
906 */
907 private boolean removeVisibleListFunctionality(Account a, UserAccounts ua) {
908 int mockAccountNum = getMockAccountNumber(a, ua);
909 if(mockAccountNum < 0) {
910 return false;
911 }
912 synchronized(ua.cacheLock) {
913 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
914 ua.mVisibleListUidToMockAccountNumbers;
915 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
916
917 /* Removing mapping from account number to account removes visible list functionality*/
918 userAcctIdToAcctMap.remove(mockAccountNum);
919
920 for(int i = userWlUidToMockAccountNums.size() - 1 ; i >= 0 ; i--) {
921 int uidKey = userWlUidToMockAccountNums.keyAt(i);
922 ArrayList<Integer> allAccountNumbers = userWlUidToMockAccountNums.get(uidKey);
923 if(allAccountNumbers != null) {
924 allAccountNumbers.remove(mockAccountNum);
925 if(allAccountNumbers.isEmpty()) {
926 userWlUidToMockAccountNums.remove(uidKey);
927 }
928 }
929 }
930 }
931 return true;
932 }
933
934 /**
935 * Sends a direct intent to a package, notifying it of a visible account. This
936 * method is a helper method of makeAccountVisible.
937 *
938 * @param desiredPackage to send Account to
939 * @param visibleAccount to send to package
940 */
941 private void sendNotification(String desiredPackage, Account visibleAccount) {
942 Intent intent = new Intent();
943 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
944 intent.setAction(NEW_ACCOUNT_VISIBLE);
945 intent.setPackage(desiredPackage);
946 intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
947 mContext.sendBroadcast(intent);
948 }
949
950 @Override
Dianne Hackborn164371f2013-10-01 19:10:13 -0700951 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
952 throws RemoteException {
953 try {
954 return super.onTransact(code, data, reply, flags);
955 } catch (RuntimeException e) {
956 // The account manager only throws security exceptions, so let's
957 // log all others.
958 if (!(e instanceof SecurityException)) {
959 Slog.wtf(TAG, "Account Manager Crash", e);
960 }
961 throw e;
962 }
963 }
964
Amith Yamasani258848d2012-08-10 17:06:33 -0700965 private UserManager getUserManager() {
966 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700967 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -0700968 }
969 return mUserManager;
970 }
971
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700972 /**
973 * Validate internal set of accounts against installed authenticators for
974 * given user. Clears cached authenticators before validating.
975 */
976 public void validateAccounts(int userId) {
977 final UserAccounts accounts = getUserAccounts(userId);
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700978 // Invalidate user-specific cache to make sure we catch any
979 // removed authenticators.
980 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
981 }
982
983 /**
984 * Validate internal set of accounts against installed authenticators for
985 * given user. Clear cached authenticators before validating when requested.
986 */
987 private void validateAccountsInternal(
988 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700989 if (Log.isLoggable(TAG, Log.DEBUG)) {
990 Log.d(TAG, "validateAccountsInternal " + accounts.userId
Fyodor Kupolov00de49e2016-09-23 13:10:27 -0700991 + " isCeDatabaseAttached=" + accounts.accountsDb.isCeDatabaseAttached()
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600992 + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700993 }
Carlos Valdiviaa46b1122016-04-26 19:36:50 -0700994
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700995 if (invalidateAuthenticatorCache) {
996 mAuthenticatorCache.invalidateCache(accounts.userId);
997 }
998
Carlos Valdiviaa46b1122016-04-26 19:36:50 -0700999 final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
1000 mAuthenticatorCache, accounts.userId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001001 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001002
Amith Yamasani04e0d262012-02-14 11:50:53 -08001003 synchronized (accounts.cacheLock) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001004 boolean accountDeleted = false;
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001005
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001006 // Get a map of stored authenticator types to UID
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001007 final AccountsDb accountsDb = accounts.accountsDb;
1008 Map<String, Integer> metaAuthUid = accountsDb.findMetaAuthUid();
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001009 // Create a list of authenticator type whose previous uid no longer exists
1010 HashSet<String> obsoleteAuthType = Sets.newHashSet();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001011 SparseBooleanArray knownUids = null;
1012 for (Entry<String, Integer> authToUidEntry : metaAuthUid.entrySet()) {
1013 String type = authToUidEntry.getKey();
1014 int uid = authToUidEntry.getValue();
1015 Integer knownUid = knownAuth.get(type);
1016 if (knownUid != null && uid == knownUid) {
1017 // Remove it from the knownAuth list if it's unchanged.
1018 knownAuth.remove(type);
1019 } else {
1020 /*
1021 * The authenticator is presently not cached and should only be triggered
1022 * when we think an authenticator has been removed (or is being updated).
1023 * But we still want to check if any data with the associated uid is
1024 * around. This is an (imperfect) signal that the package may be updating.
1025 *
1026 * A side effect of this is that an authenticator sharing a uid with
1027 * multiple apps won't get its credentials wiped as long as some app with
1028 * that uid is still on the device. But I suspect that this is a rare case.
1029 * And it isn't clear to me how an attacker could really exploit that
1030 * feature.
1031 *
1032 * The upshot is that we don't have to worry about accounts getting
1033 * uninstalled while the authenticator's package is being updated.
1034 *
1035 */
1036 if (knownUids == null) {
1037 knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001038 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001039 if (!knownUids.get(uid)) {
1040 // The authenticator is not presently available to the cache. And the
1041 // package no longer has a data directory (so we surmise it isn't updating).
1042 // So purge its data from the account databases.
1043 obsoleteAuthType.add(type);
1044 // And delete it from the TABLE_META
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001045 accountsDb.deleteMetaByAuthTypeAndUid(type, uid);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001046 }
1047 }
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001048 }
1049
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001050 // Add the newly registered authenticator to TABLE_META. If old authenticators have
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001051 // been re-enabled (after being updated for example), then we just overwrite the old
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001052 // values.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001053 for (Entry<String, Integer> entry : knownAuth.entrySet()) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001054 accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001055 }
1056
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001057 final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001058 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001059 accounts.accountCache.clear();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001060 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001061 for (Entry<Long, Account> accountEntry : accountsMap.entrySet()) {
1062 final long accountId = accountEntry.getKey();
1063 final Account account = accountEntry.getValue();
1064 if (obsoleteAuthType.contains(account.type)) {
1065 Slog.w(TAG, "deleting account " + account.name + " because type "
1066 + account.type + "'s registered authenticator no longer exist.");
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001067 accountsDb.beginTransaction();
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001068 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001069 accountsDb.deleteDeAccount(accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001070 // Also delete from CE table if user is unlocked; if user is currently
1071 // locked the account will be removed later by syncDeCeAccountsLocked
1072 if (userUnlocked) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001073 accountsDb.deleteCeAccount(accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001074 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001075 accountsDb.setTransactionSuccessful();
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001076 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001077 accountsDb.endTransaction();
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001078 }
Fred Quintana56285a62010-12-02 14:20:51 -08001079 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001080
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001081 logRecord(AccountsDb.DEBUG_ACTION_AUTHENTICATOR_REMOVE,
1082 AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001083
Amith Yamasani04e0d262012-02-14 11:50:53 -08001084 accounts.userDataCache.remove(account);
1085 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001086 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08001087 } else {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001088 ArrayList<String> accountNames = accountNamesByType.get(account.type);
Fred Quintana56285a62010-12-02 14:20:51 -08001089 if (accountNames == null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001090 accountNames = new ArrayList<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001091 accountNamesByType.put(account.type, accountNames);
Fred Quintana56285a62010-12-02 14:20:51 -08001092 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001093 accountNames.add(account.name);
Fred Quintana56285a62010-12-02 14:20:51 -08001094 }
1095 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001096 for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -08001097 final String accountType = cur.getKey();
1098 final ArrayList<String> accountNames = cur.getValue();
1099 final Account[] accountsForType = new Account[accountNames.size()];
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001100 for (int i = 0; i < accountsForType.length; i++) {
Svet Ganovf6d424f12016-09-20 20:18:53 -07001101 accountsForType[i] = new Account(accountNames.get(i), accountType,
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07001102 UUID.randomUUID().toString());
Fred Quintana56285a62010-12-02 14:20:51 -08001103 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001104 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -08001105 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001106 } finally {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001107 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001108 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001109 }
Fred Quintanaafa92b82009-12-01 16:27:03 -08001110 }
1111 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001112 }
1113
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001114 private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
1115 // Get the UIDs of all apps that might have data on the device. We want
1116 // to preserve user data if the app might otherwise be storing data.
1117 List<PackageInfo> pkgsWithData =
1118 mPackageManager.getInstalledPackagesAsUser(
1119 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
1120 SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
1121 for (PackageInfo pkgInfo : pkgsWithData) {
1122 if (pkgInfo.applicationInfo != null
1123 && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
1124 knownUids.put(pkgInfo.applicationInfo.uid, true);
1125 }
1126 }
1127 return knownUids;
1128 }
1129
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001130 static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001131 Context context,
1132 int userId) {
1133 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001134 return getAuthenticatorTypeAndUIDForUser(authCache, userId);
1135 }
1136
1137 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1138 IAccountAuthenticatorCache authCache,
1139 int userId) {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001140 HashMap<String, Integer> knownAuth = new HashMap<>();
1141 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
1142 .getAllServices(userId)) {
1143 knownAuth.put(service.type.type, service.uid);
1144 }
1145 return knownAuth;
1146 }
1147
Amith Yamasani04e0d262012-02-14 11:50:53 -08001148 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001149 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001150 }
1151
1152 protected UserAccounts getUserAccounts(int userId) {
1153 synchronized (mUsers) {
1154 UserAccounts accounts = mUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001155 boolean validateAccounts = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001156 if (accounts == null) {
Fyodor Kupolovda993802016-09-21 14:47:10 -07001157 File preNDbFile = new File(mInjector.getPreNDatabaseName(userId));
1158 File deDbFile = new File(mInjector.getDeDatabaseName(userId));
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001159 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001160 initializeDebugDbSizeAndCompileSqlStatementForLogging(accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001161 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001162 purgeOldGrants(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001163 validateAccounts = true;
1164 }
1165 // open CE database if necessary
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001166 if (!accounts.accountsDb.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001167 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
1168 synchronized (accounts.cacheLock) {
Fyodor Kupolovda993802016-09-21 14:47:10 -07001169 File ceDatabaseFile = new File(mInjector.getCeDatabaseName(userId));
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001170 accounts.accountsDb.attachCeDatabase(ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001171 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001172 syncDeCeAccountsLocked(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001173 }
1174 if (validateAccounts) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001175 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001176 }
1177 return accounts;
1178 }
1179 }
1180
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001181 private void syncDeCeAccountsLocked(UserAccounts accounts) {
1182 Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001183 List<Account> accountsToRemove = accounts.accountsDb.findCeAccountsNotInDe();
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001184 if (!accountsToRemove.isEmpty()) {
1185 Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
1186 + accounts.userId + " was locked. Removing accounts from CE tables");
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001187 logRecord(accounts, AccountsDb.DEBUG_ACTION_SYNC_DE_CE_ACCOUNTS,
1188 AccountsDb.TABLE_ACCOUNTS);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001189
1190 for (Account account : accountsToRemove) {
1191 removeAccountInternal(accounts, account, Process.myUid());
1192 }
1193 }
1194 }
1195
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001196 private void purgeOldGrantsAll() {
1197 synchronized (mUsers) {
1198 for (int i = 0; i < mUsers.size(); i++) {
1199 purgeOldGrants(mUsers.valueAt(i));
1200 }
1201 }
1202 }
1203
1204 private void purgeOldGrants(UserAccounts accounts) {
1205 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001206 List<Integer> uids = accounts.accountsDb.findAllUidGrants();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001207 for (int uid : uids) {
1208 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
1209 if (packageExists) {
1210 continue;
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001211 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001212 Log.d(TAG, "deleting grants for UID " + uid
1213 + " because its package is no longer installed");
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001214 accounts.accountsDb.deleteGrantsByUid(uid);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001215 }
1216 }
1217 }
1218
Amith Yamasani13593602012-03-22 16:16:17 -07001219 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -07001220 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -07001221 if (userId < 1) return;
1222
1223 UserAccounts accounts;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001224 boolean userUnlocked;
Amith Yamasani13593602012-03-22 16:16:17 -07001225 synchronized (mUsers) {
1226 accounts = mUsers.get(userId);
1227 mUsers.remove(userId);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001228 userUnlocked = mLocalUnlockedUsers.get(userId);
1229 mLocalUnlockedUsers.delete(userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001230 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001231 if (accounts != null) {
1232 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001233 accounts.accountsDb.close();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001234 }
Amith Yamasani13593602012-03-22 16:16:17 -07001235 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001236 Log.i(TAG, "Removing database files for user " + userId);
Fyodor Kupolovda993802016-09-21 14:47:10 -07001237 File dbFile = new File(mInjector.getDeDatabaseName(userId));
Amith Yamasani13593602012-03-22 16:16:17 -07001238
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001239 AccountsDb.deleteDbFileWarnIfFailed(dbFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001240 // Remove CE file if user is unlocked, or FBE is not enabled
1241 boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
1242 if (!fbeEnabled || userUnlocked) {
Fyodor Kupolovda993802016-09-21 14:47:10 -07001243 File ceDb = new File(mInjector.getCeDatabaseName(userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001244 if (ceDb.exists()) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001245 AccountsDb.deleteDbFileWarnIfFailed(ceDb);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001246 }
1247 }
1248 }
1249
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001250 @VisibleForTesting
1251 void onUserUnlocked(Intent intent) {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -06001252 onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
1253 }
1254
1255 void onUnlockUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001256 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1257 Log.v(TAG, "onUserUnlocked " + userId);
1258 }
1259 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001260 mLocalUnlockedUsers.put(userId, true);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001261 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001262 if (userId < 1) return;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001263 syncSharedAccounts(userId);
1264 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001265
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001266 private void syncSharedAccounts(int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001267 // Check if there's a shared account that needs to be created as an account
1268 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
1269 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -07001270 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001271 int parentUserId = UserManager.isSplitSystemUser()
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001272 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001273 : UserHandle.USER_SYSTEM;
1274 if (parentUserId < 0) {
1275 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
1276 return;
1277 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001278 for (Account sa : sharedAccounts) {
1279 if (ArrayUtils.contains(accounts, sa)) continue;
1280 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001281 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001282 }
1283 }
1284
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001285 @Override
1286 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001287 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -07001288 }
1289
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001290 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001291 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001292 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001293 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1294 Log.v(TAG, "getPassword: " + account
1295 + ", caller's uid " + Binder.getCallingUid()
1296 + ", pid " + Binder.getCallingPid());
1297 }
Fred Quintana382601f2010-03-25 12:25:10 -07001298 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001299 int userId = UserHandle.getCallingUserId();
1300 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001301 String msg = String.format(
1302 "uid %s cannot get secrets for accounts of type: %s",
1303 callingUid,
1304 account.type);
1305 throw new SecurityException(msg);
1306 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001307 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001308 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001309 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001310 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001311 } finally {
1312 restoreCallingIdentity(identityToken);
1313 }
1314 }
1315
Amith Yamasani04e0d262012-02-14 11:50:53 -08001316 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -07001317 if (account == null) {
1318 return null;
1319 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001320 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001321 Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
1322 return null;
1323 }
Fred Quintana31957f12009-10-21 13:43:10 -07001324
Amith Yamasani04e0d262012-02-14 11:50:53 -08001325 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001326 return accounts.accountsDb.findAccountPasswordByNameAndType(account.name, account.type);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001327 }
1328 }
1329
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001330 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001331 public String getPreviousName(Account account) {
1332 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1333 Log.v(TAG, "getPreviousName: " + account
1334 + ", caller's uid " + Binder.getCallingUid()
1335 + ", pid " + Binder.getCallingPid());
1336 }
1337 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001338 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001339 long identityToken = clearCallingIdentity();
1340 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001341 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001342 return readPreviousNameInternal(accounts, account);
1343 } finally {
1344 restoreCallingIdentity(identityToken);
1345 }
1346 }
1347
1348 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
1349 if (account == null) {
1350 return null;
1351 }
1352 synchronized (accounts.cacheLock) {
1353 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
1354 if (previousNameRef == null) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001355 String previousName = accounts.accountsDb.findDeAccountPreviousName(account);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001356 previousNameRef = new AtomicReference<>(previousName);
1357 accounts.previousNameCache.put(account, previousNameRef);
1358 return previousName;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001359 } else {
1360 return previousNameRef.get();
1361 }
1362 }
1363 }
1364
1365 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001366 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001367 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001368 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001369 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
1370 account, key, callingUid, Binder.getCallingPid());
1371 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -08001372 }
Fred Quintana382601f2010-03-25 12:25:10 -07001373 if (account == null) throw new IllegalArgumentException("account is null");
1374 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001375 int userId = UserHandle.getCallingUserId();
1376 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001377 String msg = String.format(
1378 "uid %s cannot get user data for accounts of type: %s",
1379 callingUid,
1380 account.type);
1381 throw new SecurityException(msg);
1382 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001383 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07001384 Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
1385 return null;
1386 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001387 long identityToken = clearCallingIdentity();
1388 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001389 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00001390 synchronized (accounts.cacheLock) {
1391 if (!accountExistsCacheLocked(accounts, account)) {
1392 return null;
1393 }
1394 return readUserDataInternalLocked(accounts, account, key);
1395 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001396 } finally {
1397 restoreCallingIdentity(identityToken);
1398 }
1399 }
1400
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001401 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001402 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001403 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001404 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1405 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001406 + "for user id " + userId
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001407 + " caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001408 + ", pid " + Binder.getCallingPid());
1409 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001410 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001411 if (isCrossUser(callingUid, userId)) {
1412 throw new SecurityException(
1413 String.format(
1414 "User %s tying to get authenticator types for %s" ,
1415 UserHandle.getCallingUserId(),
1416 userId));
1417 }
1418
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001419 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001420 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001421 return getAuthenticatorTypesInternal(userId);
1422
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001423 } finally {
1424 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001425 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001426 }
1427
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001428 /**
1429 * Should only be called inside of a clearCallingIdentity block.
1430 */
1431 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
Fyodor Kupolov81446482016-08-24 11:27:49 -07001432 mAuthenticatorCache.updateServices(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001433 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
1434 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
1435 AuthenticatorDescription[] types =
1436 new AuthenticatorDescription[authenticatorCollection.size()];
1437 int i = 0;
1438 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
1439 : authenticatorCollection) {
1440 types[i] = authenticator.type;
1441 i++;
1442 }
1443 return types;
1444 }
1445
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001446 private boolean isCrossUser(int callingUid, int userId) {
1447 return (userId != UserHandle.getCallingUserId()
1448 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001449 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001450 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1451 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001452 }
1453
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001454 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07001455 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001456 Bundle.setDefusable(extras, true);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001457 // clears the visible list functionality for this account because this method allows
1458 // default account access to all applications for account.
1459
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001460 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001461 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001462 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001463 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001464 + ", pid " + Binder.getCallingPid());
1465 }
Fred Quintana382601f2010-03-25 12:25:10 -07001466 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001467 int userId = UserHandle.getCallingUserId();
1468 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001469 String msg = String.format(
1470 "uid %s cannot explicitly add accounts of type: %s",
1471 callingUid,
1472 account.type);
1473 throw new SecurityException(msg);
1474 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001475 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001476 /*
1477 * Child users are not allowed to add accounts. Only the accounts that are
1478 * shared by the parent profile can be added to child profile.
1479 *
1480 * TODO: Only allow accounts that were shared to be added by
1481 * a limited user.
1482 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001483
Fred Quintana60307342009-03-24 22:48:12 -07001484 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001485 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001486 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001487 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001488 return addAccountInternal(accounts, account, password, extras, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -07001489 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001490 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001491 }
1492 }
1493
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001494 @Override
1495 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001496 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001497 int callingUid = Binder.getCallingUid();
1498 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1499 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001500 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001501 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001502 final UserAccounts fromAccounts = getUserAccounts(userFrom);
1503 final UserAccounts toAccounts = getUserAccounts(userTo);
1504 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001505 if (response != null) {
1506 Bundle result = new Bundle();
1507 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1508 try {
1509 response.onResult(result);
1510 } catch (RemoteException e) {
1511 Slog.w(TAG, "Failed to report error back to the client." + e);
1512 }
1513 }
1514 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -08001515 }
1516
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001517 Slog.d(TAG, "Copying account " + account.name
1518 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001519 long identityToken = clearCallingIdentity();
1520 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001521 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001522 false /* stripAuthTokenFromResult */, account.name,
1523 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001524 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001525 protected String toDebugString(long now) {
1526 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1527 + ", " + account.type;
1528 }
1529
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001530 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001531 public void run() throws RemoteException {
1532 mAuthenticator.getAccountCredentialsForCloning(this, account);
1533 }
1534
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001535 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001536 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001537 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001538 if (result != null
1539 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1540 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001541 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001542 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001543 super.onResult(result);
1544 }
1545 }
1546 }.bind();
1547 } finally {
1548 restoreCallingIdentity(identityToken);
1549 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001550 }
1551
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001552 @Override
1553 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001554 final int callingUid = Binder.getCallingUid();
1555 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1556 String msg = String.format(
1557 "accountAuthenticated( account: %s, callerUid: %s)",
1558 account,
1559 callingUid);
1560 Log.v(TAG, msg);
1561 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001562 if (account == null) {
1563 throw new IllegalArgumentException("account is null");
1564 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001565 int userId = UserHandle.getCallingUserId();
1566 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001567 String msg = String.format(
1568 "uid %s cannot notify authentication for accounts of type: %s",
1569 callingUid,
1570 account.type);
1571 throw new SecurityException(msg);
1572 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001573
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001574 if (!canUserModifyAccounts(userId, callingUid) ||
1575 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001576 return false;
1577 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001578
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001579 long identityToken = clearCallingIdentity();
1580 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001581 UserAccounts accounts = getUserAccounts(userId);
1582 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001583 } finally {
1584 restoreCallingIdentity(identityToken);
1585 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07001586 }
1587
1588 private boolean updateLastAuthenticatedTime(Account account) {
1589 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001590 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001591 return accounts.accountsDb.updateAccountLastAuthenticatedTime(account);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001592 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001593 }
1594
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001595 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001596 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1597 final int parentUserId){
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001598 Bundle.setDefusable(accountCredentials, true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001599 long id = clearCallingIdentity();
1600 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001601 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001602 false /* stripAuthTokenFromResult */, account.name,
1603 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001604 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001605 protected String toDebugString(long now) {
1606 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1607 + ", " + account.type;
1608 }
1609
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001610 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001611 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001612 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001613 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001614 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -07001615 for (Account acc : getAccounts(parentUserId,
1616 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001617 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001618 mAuthenticator.addAccountFromCredentials(
1619 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001620 break;
1621 }
1622 }
1623 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001624 }
1625
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001626 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001627 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001628 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001629 // TODO: Anything to do if if succedded?
1630 // TODO: If it failed: Show error notification? Should we remove the shadow
1631 // account to avoid retries?
1632 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001633 }
1634
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001635 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001636 public void onError(int errorCode, String errorMessage) {
1637 super.onError(errorCode, errorMessage);
1638 // TODO: Show error notification to user
1639 // TODO: Should we remove the shadow account so that it doesn't keep trying?
1640 }
1641
1642 }.bind();
1643 } finally {
1644 restoreCallingIdentity(id);
1645 }
1646 }
1647
Amith Yamasani04e0d262012-02-14 11:50:53 -08001648 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001649 Bundle extras, int callingUid) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001650 Bundle.setDefusable(extras, true);
Fred Quintana743dfad2010-07-15 10:59:25 -07001651 if (account == null) {
1652 return false;
1653 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001654 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001655 Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
1656 + " is locked. callingUid=" + callingUid);
1657 return false;
1658 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001659 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001660 accounts.accountsDb.beginTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001661 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001662 if (accounts.accountsDb.findCeAccountId(account) >= 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001663 Log.w(TAG, "insertAccountIntoDatabase: " + account
1664 + ", skipping since the account already exists");
1665 return false;
1666 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001667 long accountId = accounts.accountsDb.insertCeAccount(account, password);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001668 if (accountId < 0) {
1669 Log.w(TAG, "insertAccountIntoDatabase: " + account
1670 + ", skipping the DB insert failed");
1671 return false;
1672 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001673 // Insert into DE table
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001674 if (accounts.accountsDb.insertDeAccount(account, accountId) < 0) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001675 Log.w(TAG, "insertAccountIntoDatabase: " + account
1676 + ", skipping the DB insert failed");
1677 return false;
1678 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001679 if (extras != null) {
1680 for (String key : extras.keySet()) {
1681 final String value = extras.getString(key);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001682 if (accounts.accountsDb.insertExtra(accountId, key, value) < 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001683 Log.w(TAG, "insertAccountIntoDatabase: " + account
1684 + ", skipping since insertExtra failed for key " + key);
1685 return false;
1686 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001687 }
1688 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001689 accounts.accountsDb.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001690
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001691 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
1692 accountId, accounts, callingUid);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001693
Amith Yamasani04e0d262012-02-14 11:50:53 -08001694 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001695 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001696 accounts.accountsDb.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001697 }
Amith Yamasani5be347b2013-03-31 17:44:31 -07001698 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001699 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1700 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001701 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001702
1703 // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1704 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001705 return true;
1706 }
1707
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001708 private boolean isLocalUnlockedUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001709 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001710 return mLocalUnlockedUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001711 }
1712 }
1713
Amith Yamasani5be347b2013-03-31 17:44:31 -07001714 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001715 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -07001716 * running, then clone the account too.
1717 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001718 *
Amith Yamasani5be347b2013-03-31 17:44:31 -07001719 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001720 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -07001721 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -07001722 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001723 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001724 addSharedAccountAsUser(account, user.id);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001725 if (isLocalUnlockedUser(user.id)) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07001726 mHandler.sendMessage(mHandler.obtainMessage(
Fyodor Kupolov041232a2016-02-22 15:01:45 -08001727 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -07001728 }
1729 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001730 }
1731 }
1732
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001733 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001734 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001735 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001736 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001737 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1738 Log.v(TAG, "hasFeatures: " + account
1739 + ", response " + response
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001740 + ", features " + Arrays.toString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001741 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001742 + ", pid " + Binder.getCallingPid());
1743 }
Fred Quintana382601f2010-03-25 12:25:10 -07001744 if (response == null) throw new IllegalArgumentException("response is null");
1745 if (account == null) throw new IllegalArgumentException("account is null");
1746 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001747 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001748 checkReadAccountsPermitted(callingUid, account.type, userId,
1749 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001750
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001751 long identityToken = clearCallingIdentity();
1752 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001753 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001754 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001755 } finally {
1756 restoreCallingIdentity(identityToken);
1757 }
1758 }
1759
1760 private class TestFeaturesSession extends Session {
1761 private final String[] mFeatures;
1762 private final Account mAccount;
1763
Amith Yamasani04e0d262012-02-14 11:50:53 -08001764 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001765 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001766 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001767 true /* stripAuthTokenFromResult */, account.name,
1768 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001769 mFeatures = features;
1770 mAccount = account;
1771 }
1772
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001773 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001774 public void run() throws RemoteException {
1775 try {
1776 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1777 } catch (RemoteException e) {
1778 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1779 }
1780 }
1781
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001782 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001783 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001784 Bundle.setDefusable(result, true);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001785 IAccountManagerResponse response = getResponseAndClose();
1786 if (response != null) {
1787 try {
1788 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001789 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001790 return;
1791 }
Fred Quintana56285a62010-12-02 14:20:51 -08001792 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1793 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1794 + response);
1795 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001796 final Bundle newResult = new Bundle();
1797 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1798 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1799 response.onResult(newResult);
1800 } catch (RemoteException e) {
1801 // if the caller is dead then there is no one to care about remote exceptions
1802 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1803 Log.v(TAG, "failure while notifying response", e);
1804 }
1805 }
1806 }
1807 }
1808
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001809 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001810 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001811 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001812 + ", " + mAccount
1813 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1814 }
1815 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001816
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001817 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001818 public void renameAccount(
1819 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001820 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001821 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1822 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001823 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001824 + ", pid " + Binder.getCallingPid());
1825 }
1826 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001827 int userId = UserHandle.getCallingUserId();
1828 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001829 String msg = String.format(
1830 "uid %s cannot rename accounts of type: %s",
1831 callingUid,
1832 accountToRename.type);
1833 throw new SecurityException(msg);
1834 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001835 long identityToken = clearCallingIdentity();
1836 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001837 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001838 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001839 Bundle result = new Bundle();
1840 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1841 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07001842 result.putString(AccountManager.KEY_ACCOUNT_ACCESS_ID,
1843 resultingAccount.getAccessId());
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001844 try {
1845 response.onResult(result);
1846 } catch (RemoteException e) {
1847 Log.w(TAG, e.getMessage());
1848 }
1849 } finally {
1850 restoreCallingIdentity(identityToken);
1851 }
1852 }
1853
1854 private Account renameAccountInternal(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001855 UserAccounts accounts, Account accountToRename, String newName) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001856 Account resultAccount = null;
1857 /*
1858 * Cancel existing notifications. Let authenticators
1859 * re-post notifications as required. But we don't know if
1860 * the authenticators have bound their notifications to
1861 * now stale account name data.
1862 *
1863 * With a rename api, we might not need to do this anymore but it
1864 * shouldn't hurt.
1865 */
1866 cancelNotification(
1867 getSigninRequiredNotificationId(accounts, accountToRename),
1868 new UserHandle(accounts.userId));
1869 synchronized(accounts.credentialsPermissionNotificationIds) {
1870 for (Pair<Pair<Account, String>, Integer> pair:
1871 accounts.credentialsPermissionNotificationIds.keySet()) {
1872 if (accountToRename.equals(pair.first.first)) {
1873 int id = accounts.credentialsPermissionNotificationIds.get(pair);
1874 cancelNotification(id, new UserHandle(accounts.userId));
1875 }
1876 }
1877 }
1878 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001879 accounts.accountsDb.beginTransaction();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001880 Account renamedAccount = new Account(newName, accountToRename.type);
1881 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001882 final long accountId = accounts.accountsDb.findDeAccountId(accountToRename);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001883 if (accountId >= 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001884 accounts.accountsDb.renameCeAccount(accountId, newName);
1885 accounts.accountsDb.renameDeAccount(accountId, newName, accountToRename.name);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001886 }
1887 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001888 accounts.accountsDb.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001889 }
1890 /*
1891 * Database transaction was successful. Clean up cached
1892 * data associated with the account in the user profile.
1893 */
Svet Ganov5c4a5122016-09-23 19:29:11 -07001894 renamedAccount = insertAccountIntoCacheLocked(accounts, renamedAccount);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001895 /*
1896 * Extract the data and token caches before removing the
1897 * old account to preserve the user data associated with
1898 * the account.
1899 */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001900 Map<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1901 Map<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001902 removeAccountFromCacheLocked(accounts, accountToRename);
1903 /*
1904 * Update the cached data associated with the renamed
1905 * account.
1906 */
1907 accounts.userDataCache.put(renamedAccount, tmpData);
1908 accounts.authTokenCache.put(renamedAccount, tmpTokens);
1909 accounts.previousNameCache.put(
1910 renamedAccount,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001911 new AtomicReference<>(accountToRename.name));
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001912 resultAccount = renamedAccount;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001913
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001914 int parentUserId = accounts.userId;
1915 if (canHaveProfile(parentUserId)) {
1916 /*
1917 * Owner or system user account was renamed, rename the account for
1918 * those users with which the account was shared.
1919 */
1920 List<UserInfo> users = getUserManager().getUsers(true);
1921 for (UserInfo user : users) {
1922 if (user.isRestricted()
1923 && (user.restrictedProfileParentId == parentUserId)) {
1924 renameSharedAccountAsUser(accountToRename, newName, user.id);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001925 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001926 }
1927 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001928 sendAccountsChangedBroadcast(accounts.userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001929 }
1930 return resultAccount;
1931 }
1932
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001933 private boolean canHaveProfile(final int parentUserId) {
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001934 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001935 return userInfo != null && userInfo.canHaveProfile();
1936 }
1937
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001938 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001939 public void removeAccount(IAccountManagerResponse response, Account account,
1940 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001941 removeAccountAsUser(
1942 response,
1943 account,
1944 expectActivityLaunch,
1945 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001946 }
1947
1948 @Override
1949 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001950 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001951 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001952 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1953 Log.v(TAG, "removeAccount: " + account
1954 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001955 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001956 + ", pid " + Binder.getCallingPid()
1957 + ", for user id " + userId);
1958 }
1959 if (response == null) throw new IllegalArgumentException("response is null");
1960 if (account == null) throw new IllegalArgumentException("account is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001961 // Only allow the system process to modify accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001962 if (isCrossUser(callingUid, userId)) {
1963 throw new SecurityException(
1964 String.format(
1965 "User %s tying remove account for %s" ,
1966 UserHandle.getCallingUserId(),
1967 userId));
1968 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001969 /*
1970 * Only the system or authenticator should be allowed to remove accounts for that
1971 * authenticator. This will let users remove accounts (via Settings in the system) but not
1972 * arbitrary applications (like competing authenticators).
1973 */
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001974 UserHandle user = UserHandle.of(userId);
Ian Pedowitz358e51f2016-03-15 17:08:27 +00001975 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
1976 && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001977 String msg = String.format(
1978 "uid %s cannot remove accounts of type: %s",
1979 callingUid,
1980 account.type);
1981 throw new SecurityException(msg);
1982 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001983 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001984 try {
1985 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1986 "User cannot modify accounts");
1987 } catch (RemoteException re) {
1988 }
1989 return;
1990 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001991 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001992 try {
1993 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1994 "User cannot modify accounts of this type (policy).");
1995 } catch (RemoteException re) {
1996 }
1997 return;
1998 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001999 long identityToken = clearCallingIdentity();
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002000 UserAccounts accounts = getUserAccounts(userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002001 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002002 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002003 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08002004 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002005 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002006 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002007 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002008 }
2009 }
2010 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002011 final long accountId = accounts.accountsDb.findDeAccountId(account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002012 logRecord(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002013 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
2014 AccountsDb.TABLE_ACCOUNTS,
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002015 accountId,
2016 accounts,
2017 callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002018 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002019 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
2020 } finally {
2021 restoreCallingIdentity(identityToken);
2022 }
2023 }
2024
2025 @Override
2026 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002027 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002028 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2029 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002030 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002031 + ", pid " + Binder.getCallingPid());
2032 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002033 int userId = Binder.getCallingUserHandle().getIdentifier();
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002034 if (account == null) {
2035 /*
2036 * Null accounts should result in returning false, as per
2037 * AccountManage.addAccountExplicitly(...) java doc.
2038 */
2039 Log.e(TAG, "account is null");
2040 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002041 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002042 String msg = String.format(
2043 "uid %s cannot explicitly add accounts of type: %s",
2044 callingUid,
2045 account.type);
2046 throw new SecurityException(msg);
2047 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07002048 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002049 UserAccounts accounts = getUserAccountsForCaller();
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002050 final long accountId = accounts.accountsDb.findDeAccountId(account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002051 logRecord(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002052 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
2053 AccountsDb.TABLE_ACCOUNTS,
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002054 accountId,
2055 accounts,
2056 callingUid);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002057 long identityToken = clearCallingIdentity();
2058 try {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002059 return removeAccountInternal(accounts, account, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002060 } finally {
2061 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07002062 }
Fred Quintana60307342009-03-24 22:48:12 -07002063 }
2064
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002065 private class RemoveAccountSession extends Session {
2066 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002067 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002068 Account account, boolean expectActivityLaunch) {
2069 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002070 true /* stripAuthTokenFromResult */, account.name,
2071 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002072 mAccount = account;
2073 }
2074
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002075 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002076 protected String toDebugString(long now) {
2077 return super.toDebugString(now) + ", removeAccount"
2078 + ", account " + mAccount;
2079 }
2080
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002081 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002082 public void run() throws RemoteException {
2083 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
2084 }
2085
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002086 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002087 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002088 Bundle.setDefusable(result, true);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002089 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
2090 && !result.containsKey(AccountManager.KEY_INTENT)) {
2091 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002092 if (removalAllowed) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002093 removeAccountInternal(mAccounts, mAccount, getCallingUid());
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002094 }
2095 IAccountManagerResponse response = getResponseAndClose();
2096 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08002097 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2098 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2099 + response);
2100 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002101 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002102 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002103 try {
2104 response.onResult(result2);
2105 } catch (RemoteException e) {
2106 // ignore
2107 }
2108 }
2109 }
2110 super.onResult(result);
2111 }
2112 }
2113
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07002114 @VisibleForTesting
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002115 protected void removeAccountInternal(Account account) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002116 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
Amith Yamasani04e0d262012-02-14 11:50:53 -08002117 }
2118
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002119 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002120 boolean isChanged = false;
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002121 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002122 if (!userUnlocked) {
2123 Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
2124 + " is still locked. CE data will be removed later");
2125 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002126 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002127 accounts.accountsDb.beginTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002128 // Set to a dummy value, this will only be used if the database
2129 // transaction succeeds.
2130 long accountId = -1;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002131 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002132 accountId = accounts.accountsDb.findDeAccountId(account);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002133 if (accountId >= 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002134 accounts.accountsDb.deleteDeAccount(accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002135 if (userUnlocked) {
2136 // Delete from CE table
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002137 accounts.accountsDb.deleteCeAccount(accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002138 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002139 accounts.accountsDb.setTransactionSuccessful();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002140 isChanged = true;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002141 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002142 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002143 accounts.accountsDb.endTransaction();
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002144 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002145 if (isChanged) {
2146 removeAccountFromCacheLocked(accounts, account);
2147 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
2148 sendAccountsChangedBroadcast(accounts.userId);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002149 String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
2150 : AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
2151 logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002152 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002153 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002154 long id = Binder.clearCallingIdentity();
2155 try {
2156 int parentUserId = accounts.userId;
2157 if (canHaveProfile(parentUserId)) {
2158 // Remove from any restricted profiles that are sharing this account.
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002159 List<UserInfo> users = getUserManager().getUsers(true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002160 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002161 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002162 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002163 }
2164 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08002165 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002166 } finally {
2167 Binder.restoreCallingIdentity(id);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002168 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002169
2170 if (isChanged) {
2171 synchronized (accounts.credentialsPermissionNotificationIds) {
2172 for (Pair<Pair<Account, String>, Integer> key
2173 : accounts.credentialsPermissionNotificationIds.keySet()) {
2174 if (account.equals(key.first.first)
Svet Ganovf6d424f12016-09-20 20:18:53 -07002175 && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002176 final int uid = (Integer) key.second;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07002177 mHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002178 account, uid, false));
2179 }
2180 }
2181 }
2182 }
2183
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002184 return isChanged;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002185 }
2186
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002187 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002188 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002189 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002190 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2191 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07002192 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08002193 + ", pid " + Binder.getCallingPid());
2194 }
Fred Quintana382601f2010-03-25 12:25:10 -07002195 if (accountType == null) throw new IllegalArgumentException("accountType is null");
2196 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002197 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002198 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002199 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002200 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002201 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002202 accounts.accountsDb.beginTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002203 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002204 invalidateAuthTokenLocked(accounts, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002205 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002206 accounts.accountsDb.setTransactionSuccessful();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002207 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002208 accounts.accountsDb.endTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002209 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002210 }
Fred Quintana60307342009-03-24 22:48:12 -07002211 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002212 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002213 }
2214 }
2215
Carlos Valdivia91979be2015-05-22 14:11:35 -07002216 private void invalidateCustomTokenLocked(
2217 UserAccounts accounts,
2218 String accountType,
2219 String authToken) {
2220 if (authToken == null || accountType == null) {
2221 return;
2222 }
2223 // Also wipe out cached token in memory.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002224 accounts.accountTokenCaches.remove(accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002225 }
2226
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002227 private void invalidateAuthTokenLocked(UserAccounts accounts, String accountType,
2228 String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002229 if (authToken == null || accountType == null) {
2230 return;
2231 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002232 Cursor cursor = accounts.accountsDb.findAuthtokenForAllAccounts(accountType, authToken);
Fred Quintana33269202009-04-20 16:05:10 -07002233 try {
2234 while (cursor.moveToNext()) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002235 String authTokenId = cursor.getString(0);
Fred Quintana33269202009-04-20 16:05:10 -07002236 String accountName = cursor.getString(1);
2237 String authTokenType = cursor.getString(2);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002238 accounts.accountsDb.deleteAuthToken(authTokenId);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002239 writeAuthTokenIntoCacheLocked(
2240 accounts,
Carlos Valdivia91979be2015-05-22 14:11:35 -07002241 new Account(accountName, accountType),
2242 authTokenType,
2243 null);
Fred Quintana60307342009-03-24 22:48:12 -07002244 }
Fred Quintana33269202009-04-20 16:05:10 -07002245 } finally {
2246 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07002247 }
2248 }
2249
Carlos Valdivia91979be2015-05-22 14:11:35 -07002250 private void saveCachedToken(
2251 UserAccounts accounts,
2252 Account account,
2253 String callerPkg,
2254 byte[] callerSigDigest,
2255 String tokenType,
2256 String token,
2257 long expiryMillis) {
2258
2259 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
2260 return;
2261 }
2262 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002263 UserHandle.of(accounts.userId));
Carlos Valdivia91979be2015-05-22 14:11:35 -07002264 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002265 accounts.accountTokenCaches.put(
2266 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002267 }
2268 }
2269
Amith Yamasani04e0d262012-02-14 11:50:53 -08002270 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
2271 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07002272 if (account == null || type == null) {
2273 return false;
2274 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002275 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002276 UserHandle.of(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002277 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002278 accounts.accountsDb.beginTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002279 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002280 long accountId = accounts.accountsDb.findDeAccountId(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002281 if (accountId < 0) {
2282 return false;
2283 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002284 accounts.accountsDb.deleteAuthtokensByAccountIdAndType(accountId, type);
2285 if (accounts.accountsDb.insertAuthToken(accountId, type, authToken) >= 0) {
2286 accounts.accountsDb.setTransactionSuccessful();
2287 writeAuthTokenIntoCacheLocked(accounts, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002288 return true;
2289 }
Fred Quintana33269202009-04-20 16:05:10 -07002290 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002291 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002292 accounts.accountsDb.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07002293 }
Fred Quintana60307342009-03-24 22:48:12 -07002294 }
2295 }
2296
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002297 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002298 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002299 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002300 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2301 Log.v(TAG, "peekAuthToken: " + account
2302 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002303 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002304 + ", pid " + Binder.getCallingPid());
2305 }
Fred Quintana382601f2010-03-25 12:25:10 -07002306 if (account == null) throw new IllegalArgumentException("account is null");
2307 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002308 int userId = UserHandle.getCallingUserId();
2309 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002310 String msg = String.format(
2311 "uid %s cannot peek the authtokens associated with accounts of type: %s",
2312 callingUid,
2313 account.type);
2314 throw new SecurityException(msg);
2315 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002316 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07002317 Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
2318 + callingUid);
2319 return null;
2320 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002321 long identityToken = clearCallingIdentity();
2322 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002323 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002324 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002325 } finally {
2326 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002327 }
Fred Quintana60307342009-03-24 22:48:12 -07002328 }
2329
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002330 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002331 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002332 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002333 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2334 Log.v(TAG, "setAuthToken: " + account
2335 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002336 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002337 + ", pid " + Binder.getCallingPid());
2338 }
Fred Quintana382601f2010-03-25 12:25:10 -07002339 if (account == null) throw new IllegalArgumentException("account is null");
2340 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002341 int userId = UserHandle.getCallingUserId();
2342 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002343 String msg = String.format(
2344 "uid %s cannot set auth tokens associated with accounts of type: %s",
2345 callingUid,
2346 account.type);
2347 throw new SecurityException(msg);
2348 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002349 long identityToken = clearCallingIdentity();
2350 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002351 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002352 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002353 } finally {
2354 restoreCallingIdentity(identityToken);
2355 }
Fred Quintana60307342009-03-24 22:48:12 -07002356 }
2357
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002358 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002359 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002360 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002361 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2362 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002363 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002364 + ", pid " + Binder.getCallingPid());
2365 }
Fred Quintana382601f2010-03-25 12:25:10 -07002366 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002367 int userId = UserHandle.getCallingUserId();
2368 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002369 String msg = String.format(
2370 "uid %s cannot set secrets for accounts of type: %s",
2371 callingUid,
2372 account.type);
2373 throw new SecurityException(msg);
2374 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002375 long identityToken = clearCallingIdentity();
2376 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002377 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002378 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002379 } finally {
2380 restoreCallingIdentity(identityToken);
2381 }
Fred Quintana60307342009-03-24 22:48:12 -07002382 }
2383
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002384 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
2385 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07002386 if (account == null) {
2387 return;
2388 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002389 boolean isChanged = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002390 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002391 accounts.accountsDb.beginTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002392 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002393 final long accountId = accounts.accountsDb.findDeAccountId(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002394 if (accountId >= 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002395 accounts.accountsDb.updateCeAccountPassword(accountId, password);
2396 accounts.accountsDb.deleteAuthTokensByAccountId(accountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002397 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002398 accounts.accountTokenCaches.remove(account);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002399 accounts.accountsDb.setTransactionSuccessful();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002400 // If there is an account whose password will be updated and the database
2401 // transactions succeed, then we say that a change has occured. Even if the
2402 // new password is the same as the old and there were no authtokens to delete.
2403 isChanged = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002404 String action = (password == null || password.length() == 0) ?
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002405 AccountsDb.DEBUG_ACTION_CLEAR_PASSWORD
2406 : AccountsDb.DEBUG_ACTION_SET_PASSWORD;
2407 logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08002408 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002409 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002410 accounts.accountsDb.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002411 if (isChanged) {
2412 // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
2413 sendAccountsChangedBroadcast(accounts.userId);
2414 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002415 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002416 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07002417 }
2418
Amith Yamasani04e0d262012-02-14 11:50:53 -08002419 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08002420 Log.i(TAG, "the accounts changed, sending broadcast of "
2421 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07002422 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07002423 }
2424
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002425 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002426 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002427 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002428 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2429 Log.v(TAG, "clearPassword: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002430 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002431 + ", pid " + Binder.getCallingPid());
2432 }
Fred Quintana382601f2010-03-25 12:25:10 -07002433 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002434 int userId = UserHandle.getCallingUserId();
2435 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002436 String msg = String.format(
2437 "uid %s cannot clear passwords for accounts of type: %s",
2438 callingUid,
2439 account.type);
2440 throw new SecurityException(msg);
2441 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002442 long identityToken = clearCallingIdentity();
2443 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002444 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002445 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002446 } finally {
2447 restoreCallingIdentity(identityToken);
2448 }
Fred Quintana60307342009-03-24 22:48:12 -07002449 }
2450
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002451 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002452 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002453 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002454 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2455 Log.v(TAG, "setUserData: " + account
2456 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002457 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002458 + ", pid " + Binder.getCallingPid());
2459 }
Fred Quintana382601f2010-03-25 12:25:10 -07002460 if (key == null) throw new IllegalArgumentException("key is null");
2461 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002462 int userId = UserHandle.getCallingUserId();
2463 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002464 String msg = String.format(
2465 "uid %s cannot set user data for accounts of type: %s",
2466 callingUid,
2467 account.type);
2468 throw new SecurityException(msg);
2469 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002470 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002471 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002472 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002473 synchronized (accounts.cacheLock) {
2474 if (!accountExistsCacheLocked(accounts, account)) {
2475 return;
2476 }
2477 setUserdataInternalLocked(accounts, account, key, value);
2478 }
Fred Quintana60307342009-03-24 22:48:12 -07002479 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002480 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002481 }
2482 }
2483
Simranjit Kohli858511c2016-03-10 18:36:11 +00002484 private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
2485 if (accounts.accountCache.containsKey(account.type)) {
2486 for (Account acc : accounts.accountCache.get(account.type)) {
2487 if (acc.name.equals(account.name)) {
2488 return true;
2489 }
2490 }
2491 }
2492 return false;
2493 }
2494
2495 private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
Amith Yamasani04e0d262012-02-14 11:50:53 -08002496 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07002497 if (account == null || key == null) {
2498 return;
2499 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002500 accounts.accountsDb.beginTransaction();
Simranjit Kohli858511c2016-03-10 18:36:11 +00002501 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002502 long accountId = accounts.accountsDb.findDeAccountId(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002503 if (accountId < 0) {
2504 return;
2505 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002506 long extrasId = accounts.accountsDb.findExtrasIdByAccountId(accountId, key);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002507 if (extrasId < 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002508 extrasId = accounts.accountsDb.insertExtra(accountId, key, value);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002509 if (extrasId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002510 return;
2511 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002512 } else if (!accounts.accountsDb.updateExtra(extrasId, value)) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002513 return;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002514 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002515 writeUserDataIntoCacheLocked(accounts, account, key, value);
2516 accounts.accountsDb.setTransactionSuccessful();
Simranjit Kohli858511c2016-03-10 18:36:11 +00002517 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002518 accounts.accountsDb.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002519 }
2520 }
2521
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002522 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08002523 if (result == null) {
2524 Log.e(TAG, "the result is unexpectedly null", new Exception());
2525 }
2526 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2527 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2528 + response);
2529 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002530 try {
2531 response.onResult(result);
2532 } catch (RemoteException e) {
2533 // if the caller is dead then there is no one to care about remote
2534 // exceptions
2535 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2536 Log.v(TAG, "failure while notifying response", e);
2537 }
2538 }
2539 }
2540
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002541 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07002542 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
2543 final String authTokenType)
2544 throws RemoteException {
2545 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08002546 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2547
Fred Quintanad9640ec2012-05-23 12:37:00 -07002548 final int callingUid = getCallingUid();
2549 clearCallingIdentity();
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07002550 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07002551 throw new SecurityException("can only call from system");
2552 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002553 int userId = UserHandle.getUserId(callingUid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002554 long identityToken = clearCallingIdentity();
2555 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002556 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002557 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
2558 false /* stripAuthTokenFromResult */, null /* accountName */,
2559 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002560 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002561 protected String toDebugString(long now) {
2562 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07002563 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08002564 + ", authTokenType " + authTokenType;
2565 }
2566
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002567 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002568 public void run() throws RemoteException {
2569 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2570 }
2571
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002572 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002573 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002574 Bundle.setDefusable(result, true);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002575 if (result != null) {
2576 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
2577 Bundle bundle = new Bundle();
2578 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
2579 super.onResult(bundle);
2580 return;
2581 } else {
2582 super.onResult(result);
2583 }
2584 }
2585 }.bind();
2586 } finally {
2587 restoreCallingIdentity(identityToken);
2588 }
2589 }
2590
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002591 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07002592 public void getAuthToken(
2593 IAccountManagerResponse response,
2594 final Account account,
2595 final String authTokenType,
2596 final boolean notifyOnAuthFailure,
2597 final boolean expectActivityLaunch,
2598 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002599 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002600 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2601 Log.v(TAG, "getAuthToken: " + account
2602 + ", response " + response
2603 + ", authTokenType " + authTokenType
2604 + ", notifyOnAuthFailure " + notifyOnAuthFailure
2605 + ", expectActivityLaunch " + expectActivityLaunch
2606 + ", caller's uid " + Binder.getCallingUid()
2607 + ", pid " + Binder.getCallingPid());
2608 }
Fred Quintana382601f2010-03-25 12:25:10 -07002609 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002610 try {
2611 if (account == null) {
2612 Slog.w(TAG, "getAuthToken called with null account");
2613 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
2614 return;
2615 }
2616 if (authTokenType == null) {
2617 Slog.w(TAG, "getAuthToken called with null authTokenType");
2618 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
2619 return;
2620 }
2621 } catch (RemoteException e) {
2622 Slog.w(TAG, "Failed to report error back to the client." + e);
2623 return;
2624 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002625 int userId = UserHandle.getCallingUserId();
2626 long ident = Binder.clearCallingIdentity();
2627 final UserAccounts accounts;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002628 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002629 try {
2630 accounts = getUserAccounts(userId);
2631 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2632 AuthenticatorDescription.newKey(account.type), accounts.userId);
2633 } finally {
2634 Binder.restoreCallingIdentity(ident);
2635 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002636
Costin Manolachea40c6302010-12-13 14:50:45 -08002637 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07002638 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08002639
2640 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002641 final int callerUid = Binder.getCallingUid();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002642 final boolean permissionGranted =
2643 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08002644
Carlos Valdivia91979be2015-05-22 14:11:35 -07002645 // Get the calling package. We will use it for the purpose of caching.
2646 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07002647 List<String> callerOwnedPackageNames;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002648 ident = Binder.clearCallingIdentity();
Amith Yamasanie7360012015-06-03 17:39:40 -07002649 try {
2650 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
2651 } finally {
2652 Binder.restoreCallingIdentity(ident);
2653 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002654 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
2655 String msg = String.format(
2656 "Uid %s is attempting to illegally masquerade as package %s!",
2657 callerUid,
2658 callerPkg);
2659 throw new SecurityException(msg);
2660 }
2661
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002662 // let authenticator know the identity of the caller
2663 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
2664 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07002665
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002666 if (notifyOnAuthFailure) {
2667 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08002668 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002669
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002670 long identityToken = clearCallingIdentity();
2671 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07002672 // Distill the caller's package signatures into a single digest.
2673 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
2674
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002675 // if the caller has permission, do the peek. otherwise go the more expensive
2676 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08002677 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002678 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002679 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002680 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002681 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
2682 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2683 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002684 onResult(response, result);
2685 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002686 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002687 }
2688
Carlos Valdivia91979be2015-05-22 14:11:35 -07002689 if (customTokens) {
2690 /*
2691 * Look up tokens in the new cache only if the loginOptions don't have parameters
2692 * outside of those expected to be injected by the AccountManager, e.g.
2693 * ANDORID_PACKAGE_NAME.
2694 */
2695 String token = readCachedTokenInternal(
2696 accounts,
2697 account,
2698 authTokenType,
2699 callerPkg,
2700 callerPkgSigDigest);
2701 if (token != null) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002702 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2703 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2704 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002705 Bundle result = new Bundle();
2706 result.putString(AccountManager.KEY_AUTHTOKEN, token);
2707 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2708 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2709 onResult(response, result);
2710 return;
2711 }
2712 }
2713
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002714 new Session(
2715 accounts,
2716 response,
2717 account.type,
2718 expectActivityLaunch,
2719 false /* stripAuthTokenFromResult */,
2720 account.name,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002721 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002722 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002723 protected String toDebugString(long now) {
2724 if (loginOptions != null) loginOptions.keySet();
2725 return super.toDebugString(now) + ", getAuthToken"
2726 + ", " + account
2727 + ", authTokenType " + authTokenType
2728 + ", loginOptions " + loginOptions
2729 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2730 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002731
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002732 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002733 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002734 // If the caller doesn't have permission then create and return the
2735 // "grant permission" intent instead of the "getAuthToken" intent.
2736 if (!permissionGranted) {
2737 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2738 } else {
2739 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2740 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002741 }
2742
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002743 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002744 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002745 Bundle.setDefusable(result, true);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002746 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002747 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002748 Intent intent = newGrantCredentialsPermissionIntent(
2749 account,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002750 null,
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002751 callerUid,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002752 new AccountAuthenticatorResponse(this),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002753 authTokenType,
2754 true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002755 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002756 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002757 onResult(bundle);
2758 return;
2759 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002760 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002761 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002762 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2763 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002764 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002765 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002766 "the type and name should not be empty");
2767 return;
2768 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002769 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08002770 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002771 saveAuthTokenToDatabase(
2772 mAccounts,
2773 resultAccount,
2774 authTokenType,
2775 authToken);
2776 }
2777 long expiryMillis = result.getLong(
2778 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2779 if (customTokens
2780 && expiryMillis > System.currentTimeMillis()) {
2781 saveCachedToken(
2782 mAccounts,
2783 account,
2784 callerPkg,
2785 callerPkgSigDigest,
2786 authTokenType,
2787 authToken,
2788 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08002789 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002790 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002791
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002792 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08002793 if (intent != null && notifyOnAuthFailure && !customTokens) {
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002794 /*
2795 * Make sure that the supplied intent is owned by the authenticator
2796 * giving it to the system. Otherwise a malicious authenticator could
2797 * have users launching arbitrary activities by tricking users to
2798 * interact with malicious notifications.
2799 */
2800 checkKeyIntent(
2801 Binder.getCallingUid(),
2802 intent);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002803 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002804 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002805 intent, "android", accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002806 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002807 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002808 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07002809 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002810 }.bind();
2811 } finally {
2812 restoreCallingIdentity(identityToken);
2813 }
Fred Quintana60307342009-03-24 22:48:12 -07002814 }
2815
Carlos Valdivia91979be2015-05-22 14:11:35 -07002816 private byte[] calculatePackageSignatureDigest(String callerPkg) {
2817 MessageDigest digester;
2818 try {
2819 digester = MessageDigest.getInstance("SHA-256");
2820 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2821 callerPkg, PackageManager.GET_SIGNATURES);
2822 for (Signature sig : pkgInfo.signatures) {
2823 digester.update(sig.toByteArray());
2824 }
2825 } catch (NoSuchAlgorithmException x) {
2826 Log.wtf(TAG, "SHA-256 should be available", x);
2827 digester = null;
2828 } catch (NameNotFoundException e) {
2829 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2830 digester = null;
2831 }
2832 return (digester == null) ? null : digester.digest();
2833 }
2834
Dianne Hackborn41203752012-08-31 14:05:51 -07002835 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002836 String packageName, int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002837 int uid = intent.getIntExtra(
2838 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2839 String authTokenType = intent.getStringExtra(
2840 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
Eric Fischeree452ee2009-08-31 17:58:06 -07002841 final String titleAndSubtitle =
2842 mContext.getString(R.string.permission_request_notification_with_subtitle,
2843 account.name);
2844 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07002845 String title = titleAndSubtitle;
2846 String subtitle = "";
2847 if (index > 0) {
2848 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002849 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07002850 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002851 UserHandle user = UserHandle.of(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01002852 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04002853 Notification n = new Notification.Builder(contextForUser)
2854 .setSmallIcon(android.R.drawable.stat_sys_warning)
2855 .setWhen(0)
2856 .setColor(contextForUser.getColor(
2857 com.android.internal.R.color.system_notification_accent_color))
2858 .setContentTitle(title)
2859 .setContentText(subtitle)
2860 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2861 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2862 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002863 installNotification(getCredentialPermissionNotificationId(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002864 account, authTokenType, uid), n, packageName, user.getIdentifier());
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002865 }
2866
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002867 private Intent newGrantCredentialsPermissionIntent(Account account, String packageName,
2868 int uid, AccountAuthenticatorResponse response, String authTokenType,
2869 boolean startInNewTask) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002870
2871 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002872
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002873 if (startInNewTask) {
2874 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
2875 // Since it was set in Eclair+ we can't change it without breaking apps using
2876 // the intent from a non-Activity context. This is the default behavior.
2877 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2878 }
2879 intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account,
2880 authTokenType, uid) + (packageName != null ? packageName : "")));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002881 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002882 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2883 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002884 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002885
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002886 return intent;
2887 }
2888
2889 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2890 int uid) {
2891 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002892 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002893 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002894 final Pair<Pair<Account, String>, Integer> key =
2895 new Pair<Pair<Account, String>, Integer>(
2896 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002897 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002898 if (id == null) {
2899 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002900 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002901 }
2902 }
2903 return id;
2904 }
2905
Amith Yamasani04e0d262012-02-14 11:50:53 -08002906 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002907 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002908 synchronized (accounts.signinRequiredNotificationIds) {
2909 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002910 if (id == null) {
2911 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002912 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002913 }
2914 }
2915 return id;
2916 }
2917
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002918 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07002919 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07002920 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002921 final boolean expectActivityLaunch, final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002922 Bundle.setDefusable(optionsIn, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002923 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2924 Log.v(TAG, "addAccount: accountType " + accountType
2925 + ", response " + response
2926 + ", authTokenType " + authTokenType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002927 + ", requiredFeatures " + Arrays.toString(requiredFeatures)
Fred Quintana56285a62010-12-02 14:20:51 -08002928 + ", expectActivityLaunch " + expectActivityLaunch
2929 + ", caller's uid " + Binder.getCallingUid()
2930 + ", pid " + Binder.getCallingPid());
2931 }
Fred Quintana382601f2010-03-25 12:25:10 -07002932 if (response == null) throw new IllegalArgumentException("response is null");
2933 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002934
Amith Yamasani71e6c692013-03-24 17:39:28 -07002935 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002936 final int uid = Binder.getCallingUid();
2937 final int userId = UserHandle.getUserId(uid);
2938 if (!canUserModifyAccounts(userId, uid)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002939 try {
2940 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2941 "User is not allowed to add an account!");
2942 } catch (RemoteException re) {
2943 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002944 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002945 return;
2946 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002947 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002948 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002949 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2950 "User cannot modify accounts of this type (policy).");
2951 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002952 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002953 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2954 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002955 return;
2956 }
2957
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002958 final int pid = Binder.getCallingPid();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002959 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2960 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2961 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2962
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002963 int usrId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002964 long identityToken = clearCallingIdentity();
2965 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002966 UserAccounts accounts = getUserAccounts(usrId);
2967 logRecordWithUid(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002968 accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
2969 uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002970 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002971 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002972 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002973 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002974 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07002975 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07002976 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002977 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002978
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002979 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002980 protected String toDebugString(long now) {
2981 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07002982 + ", accountType " + accountType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002983 + ", requiredFeatures " + Arrays.toString(requiredFeatures);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002984 }
2985 }.bind();
2986 } finally {
2987 restoreCallingIdentity(identityToken);
2988 }
Fred Quintana60307342009-03-24 22:48:12 -07002989 }
2990
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002991 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002992 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
2993 final String authTokenType, final String[] requiredFeatures,
2994 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002995 Bundle.setDefusable(optionsIn, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002996 int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002997 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2998 Log.v(TAG, "addAccount: accountType " + accountType
2999 + ", response " + response
3000 + ", authTokenType " + authTokenType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003001 + ", requiredFeatures " + Arrays.toString(requiredFeatures)
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003002 + ", expectActivityLaunch " + expectActivityLaunch
3003 + ", caller's uid " + Binder.getCallingUid()
3004 + ", pid " + Binder.getCallingPid()
3005 + ", for user id " + userId);
3006 }
3007 if (response == null) throw new IllegalArgumentException("response is null");
3008 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003009 // Only allow the system process to add accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003010 if (isCrossUser(callingUid, userId)) {
3011 throw new SecurityException(
3012 String.format(
3013 "User %s trying to add account for %s" ,
3014 UserHandle.getCallingUserId(),
3015 userId));
3016 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003017
3018 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003019 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003020 try {
3021 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3022 "User is not allowed to add an account!");
3023 } catch (RemoteException re) {
3024 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003025 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003026 return;
3027 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003028 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003029 try {
3030 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3031 "User cannot modify accounts of this type (policy).");
3032 } catch (RemoteException re) {
3033 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003034 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3035 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003036 return;
3037 }
3038
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003039 final int pid = Binder.getCallingPid();
3040 final int uid = Binder.getCallingUid();
3041 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3042 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3043 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3044
3045 long identityToken = clearCallingIdentity();
3046 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003047 UserAccounts accounts = getUserAccounts(userId);
3048 logRecordWithUid(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003049 accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
3050 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003051 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003052 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003053 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003054 @Override
3055 public void run() throws RemoteException {
3056 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
3057 options);
3058 }
3059
3060 @Override
3061 protected String toDebugString(long now) {
3062 return super.toDebugString(now) + ", addAccount"
3063 + ", accountType " + accountType
3064 + ", requiredFeatures "
3065 + (requiredFeatures != null
3066 ? TextUtils.join(",", requiredFeatures)
3067 : null);
3068 }
3069 }.bind();
3070 } finally {
3071 restoreCallingIdentity(identityToken);
3072 }
3073 }
3074
Sandra Kwan78812282015-11-04 11:19:47 -08003075 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003076 public void startAddAccountSession(
3077 final IAccountManagerResponse response,
3078 final String accountType,
3079 final String authTokenType,
3080 final String[] requiredFeatures,
Sandra Kwan78812282015-11-04 11:19:47 -08003081 final boolean expectActivityLaunch,
3082 final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003083 Bundle.setDefusable(optionsIn, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003084 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3085 Log.v(TAG,
3086 "startAddAccountSession: accountType " + accountType
3087 + ", response " + response
3088 + ", authTokenType " + authTokenType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003089 + ", requiredFeatures " + Arrays.toString(requiredFeatures)
Sandra Kwan78812282015-11-04 11:19:47 -08003090 + ", expectActivityLaunch " + expectActivityLaunch
3091 + ", caller's uid " + Binder.getCallingUid()
3092 + ", pid " + Binder.getCallingPid());
3093 }
3094 if (response == null) {
3095 throw new IllegalArgumentException("response is null");
3096 }
3097 if (accountType == null) {
3098 throw new IllegalArgumentException("accountType is null");
3099 }
3100
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003101 final int uid = Binder.getCallingUid();
3102 final int userId = UserHandle.getUserId(uid);
3103 if (!canUserModifyAccounts(userId, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003104 try {
3105 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3106 "User is not allowed to add an account!");
3107 } catch (RemoteException re) {
3108 }
3109 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3110 return;
3111 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003112 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003113 try {
3114 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3115 "User cannot modify accounts of this type (policy).");
3116 } catch (RemoteException re) {
3117 }
3118 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3119 userId);
3120 return;
3121 }
Sandra Kwan78812282015-11-04 11:19:47 -08003122 final int pid = Binder.getCallingPid();
Sandra Kwan78812282015-11-04 11:19:47 -08003123 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3124 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3125 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3126
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003127 // Check to see if the Password should be included to the caller.
3128 String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3129 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003130 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003131
Sandra Kwan78812282015-11-04 11:19:47 -08003132 long identityToken = clearCallingIdentity();
3133 try {
Hongming Jin368aa192016-07-29 14:29:54 -07003134 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003135 logRecordWithUid(accounts, AccountsDb.DEBUG_ACTION_CALLED_START_ACCOUNT_ADD,
3136 AccountsDb.TABLE_ACCOUNTS, uid);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003137 new StartAccountSession(
3138 accounts,
3139 response,
3140 accountType,
3141 expectActivityLaunch,
3142 null /* accountName */,
3143 false /* authDetailsRequired */,
3144 true /* updateLastAuthenticationTime */,
3145 isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003146 @Override
3147 public void run() throws RemoteException {
3148 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
3149 requiredFeatures, options);
3150 }
3151
3152 @Override
3153 protected String toDebugString(long now) {
3154 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
3155 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
3156 + accountType + ", requiredFeatures "
3157 + (requiredFeatures != null ? requiredFeaturesStr : null);
3158 }
3159 }.bind();
3160 } finally {
3161 restoreCallingIdentity(identityToken);
3162 }
3163 }
3164
3165 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
3166 private abstract class StartAccountSession extends Session {
3167
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003168 private final boolean mIsPasswordForwardingAllowed;
3169
3170 public StartAccountSession(
3171 UserAccounts accounts,
3172 IAccountManagerResponse response,
3173 String accountType,
3174 boolean expectActivityLaunch,
3175 String accountName,
3176 boolean authDetailsRequired,
3177 boolean updateLastAuthenticationTime,
3178 boolean isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003179 super(accounts, response, accountType, expectActivityLaunch,
3180 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
3181 updateLastAuthenticationTime);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003182 mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
Sandra Kwan78812282015-11-04 11:19:47 -08003183 }
3184
3185 @Override
3186 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003187 Bundle.setDefusable(result, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003188 mNumResults++;
3189 Intent intent = null;
Sandra Kwan78812282015-11-04 11:19:47 -08003190 if (result != null
3191 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08003192 checkKeyIntent(
3193 Binder.getCallingUid(),
3194 intent);
Sandra Kwan78812282015-11-04 11:19:47 -08003195 }
Sandra Kwan78812282015-11-04 11:19:47 -08003196 IAccountManagerResponse response;
3197 if (mExpectActivityLaunch && result != null
3198 && result.containsKey(AccountManager.KEY_INTENT)) {
3199 response = mResponse;
3200 } else {
3201 response = getResponseAndClose();
3202 }
3203 if (response == null) {
3204 return;
3205 }
3206 if (result == null) {
3207 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3208 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
3209 + response);
3210 }
3211 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3212 "null bundle returned");
3213 return;
3214 }
3215
3216 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
3217 // All AccountManager error codes are greater
3218 // than 0
3219 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
3220 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3221 return;
3222 }
3223
Hongming Jin368aa192016-07-29 14:29:54 -07003224 // Omit passwords if the caller isn't permitted to see them.
3225 if (!mIsPasswordForwardingAllowed) {
3226 result.remove(AccountManager.KEY_PASSWORD);
3227 }
3228
Sandra Kwan78812282015-11-04 11:19:47 -08003229 // Strip auth token from result.
3230 result.remove(AccountManager.KEY_AUTHTOKEN);
3231
3232 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3233 Log.v(TAG,
3234 getClass().getSimpleName() + " calling onResult() on response " + response);
3235 }
3236
3237 // Get the session bundle created by authenticator. The
3238 // bundle contains data necessary for finishing the session
3239 // later. The session bundle will be encrypted here and
3240 // decrypted later when trying to finish the session.
3241 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
3242 if (sessionBundle != null) {
3243 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3244 if (TextUtils.isEmpty(accountType)
Andreas Gampe9b041742015-12-11 17:23:33 -08003245 || !mAccountType.equalsIgnoreCase(accountType)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003246 Log.w(TAG, "Account type in session bundle doesn't match request.");
3247 }
3248 // Add accountType info to session bundle. This will
3249 // override any value set by authenticator.
3250 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
3251
3252 // Encrypt session bundle before returning to caller.
3253 try {
3254 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3255 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
3256 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
3257 } catch (GeneralSecurityException e) {
3258 if (Log.isLoggable(TAG, Log.DEBUG)) {
3259 Log.v(TAG, "Failed to encrypt session bundle!", e);
3260 }
3261 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3262 "failed to encrypt session bundle");
3263 return;
3264 }
3265 }
3266
3267 sendResponse(response, result);
3268 }
3269 }
3270
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003271 @Override
Sandra Kwan0b84b452016-01-20 15:25:42 -08003272 public void finishSessionAsUser(IAccountManagerResponse response,
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003273 @NonNull Bundle sessionBundle,
3274 boolean expectActivityLaunch,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003275 Bundle appInfo,
3276 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003277 Bundle.setDefusable(sessionBundle, true);
Sandra Kwan0b84b452016-01-20 15:25:42 -08003278 int callingUid = Binder.getCallingUid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003279 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3280 Log.v(TAG,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003281 "finishSession: response "+ response
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003282 + ", expectActivityLaunch " + expectActivityLaunch
Sandra Kwan0b84b452016-01-20 15:25:42 -08003283 + ", caller's uid " + callingUid
3284 + ", caller's user id " + UserHandle.getCallingUserId()
3285 + ", pid " + Binder.getCallingPid()
3286 + ", for user id " + userId);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003287 }
3288 if (response == null) {
3289 throw new IllegalArgumentException("response is null");
3290 }
3291
3292 // Session bundle is the encrypted bundle of the original bundle created by authenticator.
3293 // Account type is added to it before encryption.
3294 if (sessionBundle == null || sessionBundle.size() == 0) {
3295 throw new IllegalArgumentException("sessionBundle is empty");
3296 }
3297
Sandra Kwan0b84b452016-01-20 15:25:42 -08003298 // Only allow the system process to finish session for other users
3299 if (isCrossUser(callingUid, userId)) {
3300 throw new SecurityException(
3301 String.format(
3302 "User %s trying to finish session for %s without cross user permission",
3303 UserHandle.getCallingUserId(),
3304 userId));
3305 }
3306
Sandra Kwan0b84b452016-01-20 15:25:42 -08003307 if (!canUserModifyAccounts(userId, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003308 sendErrorResponse(response,
3309 AccountManager.ERROR_CODE_USER_RESTRICTED,
3310 "User is not allowed to add an account!");
3311 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3312 return;
3313 }
3314
3315 final int pid = Binder.getCallingPid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003316 final Bundle decryptedBundle;
3317 final String accountType;
3318 // First decrypt session bundle to get account type for checking permission.
3319 try {
3320 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3321 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
3322 if (decryptedBundle == null) {
3323 sendErrorResponse(
3324 response,
3325 AccountManager.ERROR_CODE_BAD_REQUEST,
3326 "failed to decrypt session bundle");
3327 return;
3328 }
3329 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3330 // Account type cannot be null. This should not happen if session bundle was created
3331 // properly by #StartAccountSession.
3332 if (TextUtils.isEmpty(accountType)) {
3333 sendErrorResponse(
3334 response,
3335 AccountManager.ERROR_CODE_BAD_ARGUMENTS,
3336 "accountType is empty");
3337 return;
3338 }
3339
3340 // If by any chances, decryptedBundle contains colliding keys with
3341 // system info
3342 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
3343 // update credentials flow, we should replace with the new values of the current call.
3344 if (appInfo != null) {
3345 decryptedBundle.putAll(appInfo);
3346 }
3347
3348 // Add info that may be used by add account or update credentials flow.
Sandra Kwan0b84b452016-01-20 15:25:42 -08003349 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003350 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
3351 } catch (GeneralSecurityException e) {
3352 if (Log.isLoggable(TAG, Log.DEBUG)) {
3353 Log.v(TAG, "Failed to decrypt session bundle!", e);
3354 }
3355 sendErrorResponse(
3356 response,
3357 AccountManager.ERROR_CODE_BAD_REQUEST,
3358 "failed to decrypt session bundle");
3359 return;
3360 }
3361
Sandra Kwan0b84b452016-01-20 15:25:42 -08003362 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003363 sendErrorResponse(
3364 response,
3365 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3366 "User cannot modify accounts of this type (policy).");
3367 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3368 userId);
3369 return;
3370 }
3371
3372 long identityToken = clearCallingIdentity();
3373 try {
3374 UserAccounts accounts = getUserAccounts(userId);
3375 logRecordWithUid(
3376 accounts,
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003377 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_SESSION_FINISH,
3378 AccountsDb.TABLE_ACCOUNTS,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003379 callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003380 new Session(
3381 accounts,
3382 response,
3383 accountType,
3384 expectActivityLaunch,
3385 true /* stripAuthTokenFromResult */,
3386 null /* accountName */,
3387 false /* authDetailsRequired */,
3388 true /* updateLastAuthenticationTime */) {
3389 @Override
3390 public void run() throws RemoteException {
3391 mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
3392 }
3393
3394 @Override
3395 protected String toDebugString(long now) {
3396 return super.toDebugString(now)
3397 + ", finishSession"
3398 + ", accountType " + accountType;
3399 }
3400 }.bind();
3401 } finally {
3402 restoreCallingIdentity(identityToken);
3403 }
3404 }
3405
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003406 private void showCantAddAccount(int errorCode, int userId) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003407 final DevicePolicyManagerInternal dpmi =
3408 LocalServices.getService(DevicePolicyManagerInternal.class);
3409 Intent intent = null;
Nicolas Prevot14fc1972016-08-24 14:21:38 +01003410 if (dpmi == null) {
3411 intent = getDefaultCantAddAccountIntent(errorCode);
3412 } else if (errorCode == AccountManager.ERROR_CODE_USER_RESTRICTED) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003413 intent = dpmi.createUserRestrictionSupportIntent(userId,
3414 UserManager.DISALLOW_MODIFY_ACCOUNTS);
3415 } else if (errorCode == AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
3416 intent = dpmi.createShowAdminSupportIntent(userId, false);
3417 }
3418 if (intent == null) {
3419 intent = getDefaultCantAddAccountIntent(errorCode);
3420 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003421 long identityToken = clearCallingIdentity();
3422 try {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003423 mContext.startActivityAsUser(intent, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003424 } finally {
3425 restoreCallingIdentity(identityToken);
3426 }
3427 }
3428
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003429 /**
3430 * Called when we don't know precisely who is preventing us from adding an account.
3431 */
3432 private Intent getDefaultCantAddAccountIntent(int errorCode) {
3433 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
3434 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
3435 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3436 return cantAddAccount;
3437 }
3438
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003439 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003440 public void confirmCredentialsAsUser(
3441 IAccountManagerResponse response,
3442 final Account account,
3443 final Bundle options,
3444 final boolean expectActivityLaunch,
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003445 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003446 Bundle.setDefusable(options, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003447 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003448 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3449 Log.v(TAG, "confirmCredentials: " + account
3450 + ", response " + response
3451 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003452 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003453 + ", pid " + Binder.getCallingPid());
3454 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003455 // Only allow the system process to read accounts of other users
3456 if (isCrossUser(callingUid, userId)) {
3457 throw new SecurityException(
3458 String.format(
3459 "User %s trying to confirm account credentials for %s" ,
3460 UserHandle.getCallingUserId(),
3461 userId));
3462 }
Fred Quintana382601f2010-03-25 12:25:10 -07003463 if (response == null) throw new IllegalArgumentException("response is null");
3464 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003465 long identityToken = clearCallingIdentity();
3466 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003467 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003468 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003469 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003470 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003471 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003472 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003473 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003474 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003475 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003476 protected String toDebugString(long now) {
3477 return super.toDebugString(now) + ", confirmCredentials"
3478 + ", " + account;
3479 }
3480 }.bind();
3481 } finally {
3482 restoreCallingIdentity(identityToken);
3483 }
Fred Quintana60307342009-03-24 22:48:12 -07003484 }
3485
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003486 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003487 public void updateCredentials(IAccountManagerResponse response, final Account account,
3488 final String authTokenType, final boolean expectActivityLaunch,
3489 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003490 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003491 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3492 Log.v(TAG, "updateCredentials: " + account
3493 + ", response " + response
3494 + ", authTokenType " + authTokenType
3495 + ", expectActivityLaunch " + expectActivityLaunch
3496 + ", caller's uid " + Binder.getCallingUid()
3497 + ", pid " + Binder.getCallingPid());
3498 }
Fred Quintana382601f2010-03-25 12:25:10 -07003499 if (response == null) throw new IllegalArgumentException("response is null");
3500 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003501 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003502 long identityToken = clearCallingIdentity();
3503 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003504 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003505 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003506 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003507 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003508 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003509 public void run() throws RemoteException {
3510 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
3511 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003512 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003513 protected String toDebugString(long now) {
3514 if (loginOptions != null) loginOptions.keySet();
3515 return super.toDebugString(now) + ", updateCredentials"
3516 + ", " + account
3517 + ", authTokenType " + authTokenType
3518 + ", loginOptions " + loginOptions;
3519 }
3520 }.bind();
3521 } finally {
3522 restoreCallingIdentity(identityToken);
3523 }
Fred Quintana60307342009-03-24 22:48:12 -07003524 }
3525
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003526 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003527 public void startUpdateCredentialsSession(
3528 IAccountManagerResponse response,
3529 final Account account,
3530 final String authTokenType,
3531 final boolean expectActivityLaunch,
3532 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003533 Bundle.setDefusable(loginOptions, true);
Sandra Kwane68c37e2015-11-12 17:11:49 -08003534 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3535 Log.v(TAG,
3536 "startUpdateCredentialsSession: " + account + ", response " + response
3537 + ", authTokenType " + authTokenType + ", expectActivityLaunch "
3538 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
3539 + ", pid " + Binder.getCallingPid());
3540 }
3541 if (response == null) {
3542 throw new IllegalArgumentException("response is null");
3543 }
3544 if (account == null) {
3545 throw new IllegalArgumentException("account is null");
3546 }
Sandra Kwana578d112015-12-16 16:01:43 -08003547
3548 final int uid = Binder.getCallingUid();
Sandra Kwane68c37e2015-11-12 17:11:49 -08003549 int userId = UserHandle.getCallingUserId();
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003550
3551 // Check to see if the Password should be included to the caller.
3552 String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3553 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003554 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003555
Sandra Kwane68c37e2015-11-12 17:11:49 -08003556 long identityToken = clearCallingIdentity();
3557 try {
3558 UserAccounts accounts = getUserAccounts(userId);
3559 new StartAccountSession(
3560 accounts,
3561 response,
3562 account.type,
3563 expectActivityLaunch,
3564 account.name,
3565 false /* authDetailsRequired */,
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003566 true /* updateLastCredentialTime */,
3567 isPasswordForwardingAllowed) {
Sandra Kwane68c37e2015-11-12 17:11:49 -08003568 @Override
3569 public void run() throws RemoteException {
3570 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
3571 loginOptions);
3572 }
3573
3574 @Override
3575 protected String toDebugString(long now) {
3576 if (loginOptions != null)
3577 loginOptions.keySet();
3578 return super.toDebugString(now)
3579 + ", startUpdateCredentialsSession"
3580 + ", " + account
3581 + ", authTokenType " + authTokenType
3582 + ", loginOptions " + loginOptions;
3583 }
3584 }.bind();
3585 } finally {
3586 restoreCallingIdentity(identityToken);
3587 }
3588 }
3589
3590 @Override
Sandra Kwan390c9d22016-01-12 14:13:37 -08003591 public void isCredentialsUpdateSuggested(
3592 IAccountManagerResponse response,
3593 final Account account,
3594 final String statusToken) {
3595 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3596 Log.v(TAG,
3597 "isCredentialsUpdateSuggested: " + account + ", response " + response
3598 + ", caller's uid " + Binder.getCallingUid()
3599 + ", pid " + Binder.getCallingPid());
3600 }
3601 if (response == null) {
3602 throw new IllegalArgumentException("response is null");
3603 }
3604 if (account == null) {
3605 throw new IllegalArgumentException("account is null");
3606 }
3607 if (TextUtils.isEmpty(statusToken)) {
3608 throw new IllegalArgumentException("status token is empty");
3609 }
3610
Sandra Kwan390c9d22016-01-12 14:13:37 -08003611 int usrId = UserHandle.getCallingUserId();
3612 long identityToken = clearCallingIdentity();
3613 try {
3614 UserAccounts accounts = getUserAccounts(usrId);
3615 new Session(accounts, response, account.type, false /* expectActivityLaunch */,
3616 false /* stripAuthTokenFromResult */, account.name,
3617 false /* authDetailsRequired */) {
3618 @Override
3619 protected String toDebugString(long now) {
3620 return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
3621 + ", " + account;
3622 }
3623
3624 @Override
3625 public void run() throws RemoteException {
3626 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
3627 }
3628
3629 @Override
3630 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003631 Bundle.setDefusable(result, true);
Sandra Kwan390c9d22016-01-12 14:13:37 -08003632 IAccountManagerResponse response = getResponseAndClose();
3633 if (response == null) {
3634 return;
3635 }
3636
3637 if (result == null) {
3638 sendErrorResponse(
3639 response,
3640 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3641 "null bundle");
3642 return;
3643 }
3644
3645 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3646 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3647 + response);
3648 }
3649 // Check to see if an error occurred. We know if an error occurred because all
3650 // error codes are greater than 0.
3651 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
3652 sendErrorResponse(response,
3653 result.getInt(AccountManager.KEY_ERROR_CODE),
3654 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3655 return;
3656 }
3657 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
3658 sendErrorResponse(
3659 response,
3660 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3661 "no result in response");
3662 return;
3663 }
3664 final Bundle newResult = new Bundle();
3665 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
3666 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
3667 sendResponse(response, newResult);
3668 }
3669 }.bind();
3670 } finally {
3671 restoreCallingIdentity(identityToken);
3672 }
3673 }
3674
3675 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003676 public void editProperties(IAccountManagerResponse response, final String accountType,
3677 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003678 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003679 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3680 Log.v(TAG, "editProperties: accountType " + accountType
3681 + ", response " + response
3682 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003683 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003684 + ", pid " + Binder.getCallingPid());
3685 }
Fred Quintana382601f2010-03-25 12:25:10 -07003686 if (response == null) throw new IllegalArgumentException("response is null");
3687 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003688 int userId = UserHandle.getCallingUserId();
3689 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003690 String msg = String.format(
3691 "uid %s cannot edit authenticator properites for account type: %s",
3692 callingUid,
3693 accountType);
3694 throw new SecurityException(msg);
3695 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003696 long identityToken = clearCallingIdentity();
3697 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003698 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003699 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003700 true /* stripAuthTokenFromResult */, null /* accountName */,
3701 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003702 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003703 public void run() throws RemoteException {
3704 mAuthenticator.editProperties(this, mAccountType);
3705 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003706 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003707 protected String toDebugString(long now) {
3708 return super.toDebugString(now) + ", editProperties"
3709 + ", accountType " + accountType;
3710 }
3711 }.bind();
3712 } finally {
3713 restoreCallingIdentity(identityToken);
3714 }
Fred Quintana60307342009-03-24 22:48:12 -07003715 }
3716
Amith Yamasani12747872015-12-07 14:19:49 -08003717 @Override
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003718 public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName,
3719 @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003720 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003721 throw new SecurityException("Can be called only by system UID");
3722 }
3723 Preconditions.checkNotNull(account, "account cannot be null");
3724 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3725 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3726
3727 final int userId = userHandle.getIdentifier();
3728
3729 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3730
3731 try {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003732 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
Svet Ganovf6d424f12016-09-20 20:18:53 -07003733 return hasAccountAccess(account, packageName, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003734 } catch (NameNotFoundException e) {
3735 return false;
3736 }
3737 }
3738
Svet Ganovf6d424f12016-09-20 20:18:53 -07003739 private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
3740 int uid) {
3741 if (packageName == null) {
3742 String[] packageNames = mPackageManager.getPackagesForUid(uid);
3743 if (ArrayUtils.isEmpty(packageNames)) {
3744 return false;
3745 }
3746 // For app op checks related to permissions all packages in the UID
3747 // have the same app op state, so doesn't matter which one we pick.
3748 packageName = packageNames[0];
3749 }
3750
3751 // Use null token which means any token. Having a token means the package
3752 // is trusted by the authenticator, hence it is fine to access the account.
3753 if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) {
3754 return true;
3755 }
3756 // In addition to the permissions required to get an auth token we also allow
3757 // the account to be accessed by holders of the get accounts permissions.
3758 return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
3759 || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
3760 }
3761
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003762 private boolean checkUidPermission(String permission, int uid, String opPackageName) {
3763 final long identity = Binder.clearCallingIdentity();
3764 try {
3765 IPackageManager pm = ActivityThread.getPackageManager();
3766 if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
3767 return false;
3768 }
3769 final int opCode = AppOpsManager.permissionToOpCode(permission);
3770 return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
3771 opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
3772 } catch (RemoteException e) {
3773 /* ignore - local call */
3774 } finally {
3775 Binder.restoreCallingIdentity(identity);
3776 }
3777 return false;
3778 }
3779
3780 @Override
3781 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
3782 @NonNull String packageName, @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003783 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003784 throw new SecurityException("Can be called only by system UID");
3785 }
3786
3787 Preconditions.checkNotNull(account, "account cannot be null");
3788 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3789 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3790
3791 final int userId = userHandle.getIdentifier();
3792
3793 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3794
3795 final int uid;
3796 try {
3797 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3798 } catch (NameNotFoundException e) {
3799 Slog.e(TAG, "Unknown package " + packageName);
3800 return null;
3801 }
3802
3803 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
3804
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003805 final long identity = Binder.clearCallingIdentity();
3806 try {
3807 return PendingIntent.getActivityAsUser(
3808 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
3809 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3810 null, new UserHandle(userId)).getIntentSender();
3811 } finally {
3812 Binder.restoreCallingIdentity(identity);
3813 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003814 }
3815
3816 private Intent newRequestAccountAccessIntent(Account account, String packageName,
3817 int uid, RemoteCallback callback) {
3818 return newGrantCredentialsPermissionIntent(account, packageName, uid,
3819 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
3820 @Override
3821 public void onResult(Bundle value) throws RemoteException {
3822 handleAuthenticatorResponse(true);
3823 }
3824
3825 @Override
3826 public void onRequestContinued() {
3827 /* ignore */
3828 }
3829
3830 @Override
3831 public void onError(int errorCode, String errorMessage) throws RemoteException {
3832 handleAuthenticatorResponse(false);
3833 }
3834
3835 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
3836 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -07003837 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003838 UserHandle.getUserHandleForUid(uid));
3839 if (callback != null) {
3840 Bundle result = new Bundle();
3841 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
3842 callback.sendResult(result);
3843 }
3844 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07003845 }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003846 }
3847
3848 @Override
Amith Yamasani12747872015-12-07 14:19:49 -08003849 public boolean someUserHasAccount(@NonNull final Account account) {
3850 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
3851 throw new SecurityException("Only system can check for accounts across users");
3852 }
3853 final long token = Binder.clearCallingIdentity();
3854 try {
3855 AccountAndUser[] allAccounts = getAllAccounts();
3856 for (int i = allAccounts.length - 1; i >= 0; i--) {
3857 if (allAccounts[i].account.equals(account)) {
3858 return true;
3859 }
3860 }
3861 return false;
3862 } finally {
3863 Binder.restoreCallingIdentity(token);
3864 }
3865 }
3866
Fred Quintana33269202009-04-20 16:05:10 -07003867 private class GetAccountsByTypeAndFeatureSession extends Session {
3868 private final String[] mFeatures;
3869 private volatile Account[] mAccountsOfType = null;
3870 private volatile ArrayList<Account> mAccountsWithFeatures = null;
3871 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003872 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07003873
Amith Yamasani04e0d262012-02-14 11:50:53 -08003874 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003875 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003876 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003877 true /* stripAuthTokenFromResult */, null /* accountName */,
3878 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003879 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07003880 mFeatures = features;
3881 }
3882
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003883 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003884 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003885 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003886 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
3887 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003888 }
Fred Quintana33269202009-04-20 16:05:10 -07003889 // check whether each account matches the requested features
Tejas Khorana5edff3b2016-06-28 20:59:52 -07003890 mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
Fred Quintana33269202009-04-20 16:05:10 -07003891 mCurrentAccount = 0;
3892
3893 checkAccount();
3894 }
3895
3896 public void checkAccount() {
3897 if (mCurrentAccount >= mAccountsOfType.length) {
3898 sendResult();
3899 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07003900 }
Fred Quintana33269202009-04-20 16:05:10 -07003901
Fred Quintana29e94b82010-03-10 12:11:51 -08003902 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
3903 if (accountAuthenticator == null) {
3904 // It is possible that the authenticator has died, which is indicated by
3905 // mAuthenticator being set to null. If this happens then just abort.
3906 // There is no need to send back a result or error in this case since
3907 // that already happened when mAuthenticator was cleared.
3908 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3909 Log.v(TAG, "checkAccount: aborting session since we are no longer"
3910 + " connected to the authenticator, " + toDebugString());
3911 }
3912 return;
3913 }
Fred Quintana33269202009-04-20 16:05:10 -07003914 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08003915 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07003916 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003917 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07003918 }
3919 }
3920
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003921 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003922 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003923 Bundle.setDefusable(result, true);
Fred Quintana33269202009-04-20 16:05:10 -07003924 mNumResults++;
3925 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003926 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07003927 return;
3928 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003929 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07003930 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3931 }
3932 mCurrentAccount++;
3933 checkAccount();
3934 }
3935
3936 public void sendResult() {
3937 IAccountManagerResponse response = getResponseAndClose();
3938 if (response != null) {
3939 try {
3940 Account[] accounts = new Account[mAccountsWithFeatures.size()];
3941 for (int i = 0; i < accounts.length; i++) {
3942 accounts[i] = mAccountsWithFeatures.get(i);
3943 }
Fred Quintana56285a62010-12-02 14:20:51 -08003944 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3945 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3946 + response);
3947 }
Fred Quintana33269202009-04-20 16:05:10 -07003948 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003949 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07003950 response.onResult(result);
3951 } catch (RemoteException e) {
3952 // if the caller is dead then there is no one to care about remote exceptions
3953 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3954 Log.v(TAG, "failure while notifying response", e);
3955 }
3956 }
3957 }
3958 }
3959
3960
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003961 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003962 protected String toDebugString(long now) {
3963 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3964 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3965 }
3966 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003967
Amith Yamasani04e0d262012-02-14 11:50:53 -08003968 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003969 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08003970 * @hide
3971 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003972 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003973 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003974 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07003975 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3976 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003977 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003978 return new Account[0];
3979 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003980 long identityToken = clearCallingIdentity();
3981 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003982 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003983 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003984 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003985 callingUid,
3986 null, // packageName
3987 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003988 } finally {
3989 restoreCallingIdentity(identityToken);
3990 }
3991 }
3992
Amith Yamasanif29f2362012-04-05 18:29:52 -07003993 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003994 * Returns accounts for all running users.
3995 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07003996 * @hide
3997 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003998 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003999 public AccountAndUser[] getRunningAccounts() {
4000 final int[] runningUserIds;
4001 try {
4002 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
4003 } catch (RemoteException e) {
4004 // Running in system_server; should never happen
4005 throw new RuntimeException(e);
4006 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004007 return getAccounts(runningUserIds);
4008 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07004009
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004010 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004011 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004012 public AccountAndUser[] getAllAccounts() {
Amith Yamasanid04aaa32016-06-13 12:09:36 -07004013 final List<UserInfo> users = getUserManager().getUsers(true);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004014 final int[] userIds = new int[users.size()];
4015 for (int i = 0; i < userIds.length; i++) {
4016 userIds[i] = users.get(i).id;
4017 }
4018 return getAccounts(userIds);
4019 }
4020
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004021 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004022 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004023 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004024 for (int userId : userIds) {
4025 UserAccounts userAccounts = getUserAccounts(userId);
4026 if (userAccounts == null) continue;
4027 synchronized (userAccounts.cacheLock) {
4028 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
4029 Binder.getCallingUid(), null);
4030 for (int a = 0; a < accounts.length; a++) {
4031 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07004032 }
4033 }
4034 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004035
4036 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
4037 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07004038 }
4039
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004040 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004041 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004042 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
4043 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004044 }
4045
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004046 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004047 private Account[] getAccountsAsUser(
4048 String type,
4049 int userId,
4050 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004051 int packageUid,
4052 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004053 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004054 // Only allow the system process to read accounts of other users
4055 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07004056 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08004057 && mContext.checkCallingOrSelfPermission(
4058 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
4059 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004060 throw new SecurityException("User " + UserHandle.getCallingUserId()
4061 + " trying to get account for " + userId);
4062 }
4063
Fred Quintana56285a62010-12-02 14:20:51 -08004064 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4065 Log.v(TAG, "getAccounts: accountType " + type
4066 + ", caller's uid " + Binder.getCallingUid()
4067 + ", pid " + Binder.getCallingPid());
4068 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004069 // If the original calling app was using the framework account chooser activity, we'll
4070 // be passed in the original caller's uid here, which is what should be used for filtering.
4071 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
4072 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07004073 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07004074 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004075 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4076 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004077 if (visibleAccountTypes.isEmpty()
4078 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004079 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004080 } else if (visibleAccountTypes.contains(type)) {
4081 // Prune the list down to just the requested type.
4082 visibleAccountTypes = new ArrayList<>();
4083 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004084 } // else aggregate all the visible accounts (it won't matter if the
4085 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004086
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004087 long identityToken = clearCallingIdentity();
4088 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004089 UserAccounts accounts = getUserAccounts(userId);
Tejas Khorana69990d92016-08-03 11:19:40 -07004090 Account[] accountsToReturn = getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004091 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004092 callingUid,
4093 callingPackage,
4094 visibleAccountTypes);
Tejas Khorana69990d92016-08-03 11:19:40 -07004095 ArrayList<Account> accountsToReturnList = new
4096 ArrayList<Account>(Arrays.asList(accountsToReturn));
4097 for(int i = accountsToReturnList.size() - 1; i >= 0 ; i--) {
4098 // if account not visible to caller or managed by caller, remove from
4099 // accounts to return. Note that all accounts visible by default unless
4100 // visible list functionality implemented
4101 if(!(isAccountVisible(accountsToReturnList.get(i), callingUid,
4102 getUserAccounts(userId)))) {
4103 accountsToReturnList.remove(i);
4104 }
4105 }
4106 return accountsToReturnList.toArray(new Account[accountsToReturnList.size()]);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004107 } finally {
4108 restoreCallingIdentity(identityToken);
4109 }
4110 }
4111
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004112 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004113 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004114 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004115 int callingUid,
4116 String callingPackage,
4117 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004118 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004119 ArrayList<Account> visibleAccounts = new ArrayList<>();
4120 for (String visibleType : visibleAccountTypes) {
4121 Account[] accountsForType = getAccountsFromCacheLocked(
4122 userAccounts, visibleType, callingUid, callingPackage);
4123 if (accountsForType != null) {
4124 visibleAccounts.addAll(Arrays.asList(accountsForType));
4125 }
4126 }
4127 Account[] result = new Account[visibleAccounts.size()];
4128 for (int i = 0; i < visibleAccounts.size(); i++) {
4129 result[i] = visibleAccounts.get(i);
4130 }
4131 return result;
4132 }
4133 }
4134
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004135 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004136 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07004137 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004138 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
4139 for (Account account : accounts) {
4140 addSharedAccountAsUser(account, userId);
4141 }
4142 }
4143
4144 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004145 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004146 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004147 accounts.accountsDb.deleteSharedAccount(account);
4148 long accountId = accounts.accountsDb.insertSharedAccount(account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004149 if (accountId < 0) {
4150 Log.w(TAG, "insertAccountIntoDatabase: " + account
4151 + ", skipping the DB insert failed");
4152 return false;
4153 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004154 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_SHARED_ACCOUNTS, accountId,
4155 accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004156 return true;
4157 }
4158
4159 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004160 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
4161 userId = handleIncomingUser(userId);
4162 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004163 long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account);
4164 int r = accounts.accountsDb.renameSharedAccount(account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004165 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004166 int callingUid = getCallingUid();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004167 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_RENAME, AccountsDb.TABLE_SHARED_ACCOUNTS,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004168 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004169 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004170 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004171 }
4172 return r > 0;
4173 }
4174
4175 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08004176 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004177 return removeSharedAccountAsUser(account, userId, getCallingUid());
4178 }
4179
4180 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004181 userId = handleIncomingUser(userId);
4182 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004183 long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account);
4184 boolean deleted = accounts.accountsDb.deleteSharedAccount(account);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004185 if (deleted) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004186 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE, AccountsDb.TABLE_SHARED_ACCOUNTS,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004187 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07004188 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004189 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004190 return deleted;
Amith Yamasani67df64b2012-12-14 12:09:36 -08004191 }
4192
4193 @Override
4194 public Account[] getSharedAccountsAsUser(int userId) {
4195 userId = handleIncomingUser(userId);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004196 UserAccounts accounts = getUserAccounts(userId);
4197 List<Account> accountList = accounts.accountsDb.getSharedAccounts();
Amith Yamasani67df64b2012-12-14 12:09:36 -08004198 Account[] accountArray = new Account[accountList.size()];
4199 accountList.toArray(accountArray);
4200 return accountArray;
4201 }
4202
4203 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004204 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004205 public Account[] getAccounts(String type, String opPackageName) {
Tejas Khorana69990d92016-08-03 11:19:40 -07004206 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004207 }
4208
Amith Yamasani27db4682013-03-30 17:07:47 -07004209 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004210 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004211 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004212 int callingUid = Binder.getCallingUid();
4213 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
4214 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
4215 + callingUid + " with uid=" + uid);
4216 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004217 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
4218 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004219 }
4220
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004221 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004222 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004223 public Account[] getAccountsByTypeForPackage(String type, String packageName,
4224 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004225 int packageUid = -1;
4226 try {
4227 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07004228 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
4229 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004230 } catch (RemoteException re) {
4231 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
4232 return new Account[0];
4233 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004234 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
4235 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004236 }
4237
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004238 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004239 public void getAccountsByFeatures(
4240 IAccountManagerResponse response,
4241 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004242 String[] features,
4243 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004244 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08004245 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4246 Log.v(TAG, "getAccounts: accountType " + type
4247 + ", response " + response
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004248 + ", features " + Arrays.toString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004249 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08004250 + ", pid " + Binder.getCallingPid());
4251 }
Fred Quintana382601f2010-03-25 12:25:10 -07004252 if (response == null) throw new IllegalArgumentException("response is null");
4253 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004254 int userId = UserHandle.getCallingUserId();
4255
Svetoslavf3f02ac2015-09-08 14:36:35 -07004256 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4257 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004258 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004259 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004260 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004261 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
4262 try {
4263 response.onResult(result);
4264 } catch (RemoteException e) {
4265 Log.e(TAG, "Cannot respond to caller do to exception." , e);
4266 }
4267 return;
4268 }
Fred Quintana33269202009-04-20 16:05:10 -07004269 long identityToken = clearCallingIdentity();
4270 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004271 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004272 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004273 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004274 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004275 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004276 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08004277 Bundle result = new Bundle();
4278 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4279 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004280 return;
4281 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004282 new GetAccountsByTypeAndFeatureSession(
4283 userAccounts,
4284 response,
4285 type,
4286 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004287 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07004288 } finally {
4289 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07004290 }
4291 }
4292
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07004293 @Override
4294 public void onAccountAccessed(String token) throws RemoteException {
4295 final int uid = Binder.getCallingUid();
4296 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
4297 return;
4298 }
4299 final int userId = UserHandle.getCallingUserId();
4300 final long identity = Binder.clearCallingIdentity();
4301 try {
4302 for (Account account : getAccounts(userId, mContext.getOpPackageName())) {
4303 if (Objects.equals(account.getAccessId(), token)) {
4304 // An app just accessed the account. At this point it knows about
4305 // it and there is not need to hide this account from the app.
4306 if (!hasAccountAccess(account, null, uid)) {
4307 updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
4308 uid, true);
4309 }
4310 }
4311 }
4312 } finally {
4313 Binder.restoreCallingIdentity(identity);
4314 }
4315 }
4316
Fred Quintanaa698f422009-04-08 19:14:54 -07004317 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07004318 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07004319 IAccountManagerResponse mResponse;
4320 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004321 final boolean mExpectActivityLaunch;
4322 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004323 final String mAccountName;
4324 // Indicates if we need to add auth details(like last credential time)
4325 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004326 // If set, we need to update the last authenticated time. This is
4327 // currently
4328 // used on
4329 // successful confirming credentials.
4330 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07004331
Fred Quintana33269202009-04-20 16:05:10 -07004332 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07004333 private int mNumRequestContinued = 0;
4334 private int mNumErrors = 0;
4335
Fred Quintana60307342009-03-24 22:48:12 -07004336 IAccountAuthenticator mAuthenticator = null;
4337
Fred Quintana8570f742010-02-18 10:32:54 -08004338 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004339 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004340
Amith Yamasani04e0d262012-02-14 11:50:53 -08004341 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004342 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4343 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004344 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4345 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4346 }
4347
4348 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4349 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4350 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07004351 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08004352 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07004353 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08004354 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004355 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07004356 mResponse = response;
4357 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004358 mExpectActivityLaunch = expectActivityLaunch;
4359 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004360 mAccountName = accountName;
4361 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004362 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004363
Fred Quintanaa698f422009-04-08 19:14:54 -07004364 synchronized (mSessions) {
4365 mSessions.put(toString(), this);
4366 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004367 if (response != null) {
4368 try {
4369 response.asBinder().linkToDeath(this, 0 /* flags */);
4370 } catch (RemoteException e) {
4371 mResponse = null;
4372 binderDied();
4373 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004374 }
Fred Quintana60307342009-03-24 22:48:12 -07004375 }
4376
Fred Quintanaa698f422009-04-08 19:14:54 -07004377 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07004378 if (mResponse == null) {
4379 // this session has already been closed
4380 return null;
4381 }
Fred Quintana60307342009-03-24 22:48:12 -07004382 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07004383 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07004384 return response;
4385 }
4386
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004387 /**
4388 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4389 * security policy.
4390 *
4391 * In particular we want to make sure that the Authenticator doesn't try to trick users
4392 * into launching aribtrary intents on the device via by tricking to click authenticator
4393 * supplied entries in the system Settings app.
4394 */
4395 protected void checkKeyIntent(
4396 int authUid,
4397 Intent intent) throws SecurityException {
4398 long bid = Binder.clearCallingIdentity();
4399 try {
4400 PackageManager pm = mContext.getPackageManager();
4401 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4402 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4403 int targetUid = targetActivityInfo.applicationInfo.uid;
Sandra Kwan0e961a12016-06-30 14:34:01 -07004404 if (!GrantCredentialsPermissionActivity.class.getName().equals(
4405 targetActivityInfo.getClass().getName())
4406 && !CantAddAccountActivity.class
4407 .equals(targetActivityInfo.getClass().getName())
4408 && PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
4409 targetUid)) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004410 String pkgName = targetActivityInfo.packageName;
4411 String activityName = targetActivityInfo.name;
4412 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4413 + "does not share a signature with the supplying authenticator (%s).";
4414 throw new SecurityException(
4415 String.format(tmpl, activityName, pkgName, mAccountType));
4416 }
4417 } finally {
4418 Binder.restoreCallingIdentity(bid);
4419 }
4420 }
4421
Fred Quintanaa698f422009-04-08 19:14:54 -07004422 private void close() {
4423 synchronized (mSessions) {
4424 if (mSessions.remove(toString()) == null) {
4425 // the session was already closed, so bail out now
4426 return;
4427 }
4428 }
4429 if (mResponse != null) {
4430 // stop listening for response deaths
4431 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4432
4433 // clear this so that we don't accidentally send any further results
4434 mResponse = null;
4435 }
4436 cancelTimeout();
4437 unbind();
4438 }
4439
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004440 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004441 public void binderDied() {
4442 mResponse = null;
4443 close();
4444 }
4445
4446 protected String toDebugString() {
4447 return toDebugString(SystemClock.elapsedRealtime());
4448 }
4449
4450 protected String toDebugString(long now) {
4451 return "Session: expectLaunch " + mExpectActivityLaunch
4452 + ", connected " + (mAuthenticator != null)
4453 + ", stats (" + mNumResults + "/" + mNumRequestContinued
4454 + "/" + mNumErrors + ")"
4455 + ", lifetime " + ((now - mCreationTime) / 1000.0);
4456 }
4457
Fred Quintana60307342009-03-24 22:48:12 -07004458 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004459 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4460 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4461 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004462 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004463 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004464 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07004465 }
4466 }
4467
4468 private void unbind() {
4469 if (mAuthenticator != null) {
4470 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07004471 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07004472 }
4473 }
4474
Fred Quintana60307342009-03-24 22:48:12 -07004475 public void cancelTimeout() {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004476 mHandler.removeMessages(MESSAGE_TIMED_OUT, this);
Fred Quintana60307342009-03-24 22:48:12 -07004477 }
4478
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004479 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004480 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07004481 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07004482 try {
4483 run();
4484 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004485 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07004486 "remote exception");
4487 }
Fred Quintana60307342009-03-24 22:48:12 -07004488 }
4489
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004490 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004491 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004492 mAuthenticator = null;
4493 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004494 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004495 try {
4496 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4497 "disconnected");
4498 } catch (RemoteException e) {
4499 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4500 Log.v(TAG, "Session.onServiceDisconnected: "
4501 + "caught RemoteException while responding", e);
4502 }
4503 }
Fred Quintana60307342009-03-24 22:48:12 -07004504 }
4505 }
4506
Fred Quintanab839afc2009-10-14 15:57:28 -07004507 public abstract void run() throws RemoteException;
4508
Fred Quintana60307342009-03-24 22:48:12 -07004509 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004510 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004511 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004512 try {
4513 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4514 "timeout");
4515 } catch (RemoteException e) {
4516 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4517 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4518 e);
4519 }
4520 }
Fred Quintana60307342009-03-24 22:48:12 -07004521 }
4522 }
4523
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004524 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004525 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004526 Bundle.setDefusable(result, true);
Fred Quintanaa698f422009-04-08 19:14:54 -07004527 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004528 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004529 if (result != null) {
4530 boolean isSuccessfulConfirmCreds = result.getBoolean(
4531 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004532 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004533 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4534 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004535 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004536 // mUpdateLastAuthenticatedTime is true and the confirmRequest
4537 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07004538 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004539 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004540 if (needUpdate || mAuthDetailsRequired) {
4541 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4542 if (needUpdate && accountPresent) {
4543 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4544 }
4545 if (mAuthDetailsRequired) {
4546 long lastAuthenticatedTime = -1;
4547 if (accountPresent) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004548 lastAuthenticatedTime = mAccounts.accountsDb
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004549 .findAccountLastAuthenticatedTime(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004550 new Account(mAccountName, mAccountType));
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004551 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07004552 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004553 lastAuthenticatedTime);
4554 }
4555 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004556 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004557 if (result != null
4558 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004559 checkKeyIntent(
4560 Binder.getCallingUid(),
4561 intent);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004562 }
4563 if (result != null
4564 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004565 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4566 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004567 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4568 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004569 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4570 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004571 }
Fred Quintana60307342009-03-24 22:48:12 -07004572 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004573 IAccountManagerResponse response;
4574 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004575 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004576 response = mResponse;
4577 } else {
4578 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004579 }
Fred Quintana60307342009-03-24 22:48:12 -07004580 if (response != null) {
4581 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07004582 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08004583 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4584 Log.v(TAG, getClass().getSimpleName()
4585 + " calling onError() on response " + response);
4586 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004587 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07004588 "null bundle returned");
4589 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08004590 if (mStripAuthTokenFromResult) {
4591 result.remove(AccountManager.KEY_AUTHTOKEN);
4592 }
Fred Quintana56285a62010-12-02 14:20:51 -08004593 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4594 Log.v(TAG, getClass().getSimpleName()
4595 + " calling onResult() on response " + response);
4596 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004597 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4598 (intent == null)) {
4599 // All AccountManager error codes are greater than 0
4600 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4601 result.getString(AccountManager.KEY_ERROR_MESSAGE));
4602 } else {
4603 response.onResult(result);
4604 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004605 }
Fred Quintana60307342009-03-24 22:48:12 -07004606 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004607 // if the caller is dead then there is no one to care about remote exceptions
4608 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4609 Log.v(TAG, "failure while notifying response", e);
4610 }
Fred Quintana60307342009-03-24 22:48:12 -07004611 }
4612 }
4613 }
Fred Quintana60307342009-03-24 22:48:12 -07004614
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004615 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004616 public void onRequestContinued() {
4617 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07004618 }
4619
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004620 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004621 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004622 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07004623 IAccountManagerResponse response = getResponseAndClose();
4624 if (response != null) {
4625 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08004626 Log.v(TAG, getClass().getSimpleName()
4627 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07004628 }
4629 try {
4630 response.onError(errorCode, errorMessage);
4631 } catch (RemoteException e) {
4632 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4633 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4634 }
4635 }
4636 } else {
4637 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4638 Log.v(TAG, "Session.onError: already closed");
4639 }
Fred Quintana60307342009-03-24 22:48:12 -07004640 }
4641 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004642
4643 /**
4644 * find the component name for the authenticator and initiate a bind
4645 * if no authenticator or the bind fails then return false, otherwise return true
4646 */
4647 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004648 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4649 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4650 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07004651 if (authenticatorInfo == null) {
4652 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4653 Log.v(TAG, "there is no authenticator for " + authenticatorType
4654 + ", bailing out");
4655 }
4656 return false;
4657 }
4658
Jeff Sharkeyce18c812016-04-27 16:00:41 -06004659 if (!isLocalUnlockedUser(mAccounts.userId)
Jeff Sharkey8a372a02016-03-16 16:25:45 -06004660 && !authenticatorInfo.componentInfo.directBootAware) {
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07004661 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4662 + " which isn't encryption aware");
4663 return false;
4664 }
4665
Fred Quintanab839afc2009-10-14 15:57:28 -07004666 Intent intent = new Intent();
4667 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4668 intent.setComponent(authenticatorInfo.componentName);
4669 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4670 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4671 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08004672 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004673 UserHandle.of(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07004674 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4675 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4676 }
4677 return false;
4678 }
4679
Fred Quintanab839afc2009-10-14 15:57:28 -07004680 return true;
4681 }
Fred Quintana60307342009-03-24 22:48:12 -07004682 }
4683
Svet Ganov5d09c992016-09-07 09:57:41 -07004684 class MessageHandler extends Handler {
Fred Quintana60307342009-03-24 22:48:12 -07004685 MessageHandler(Looper looper) {
4686 super(looper);
4687 }
Costin Manolache3348f142009-09-29 18:58:36 -07004688
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004689 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004690 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07004691 switch (msg.what) {
4692 case MESSAGE_TIMED_OUT:
4693 Session session = (Session)msg.obj;
4694 session.onTimedOut();
4695 break;
4696
Amith Yamasani5be347b2013-03-31 17:44:31 -07004697 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00004698 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07004699 break;
4700
Fred Quintana60307342009-03-24 22:48:12 -07004701 default:
4702 throw new IllegalStateException("unhandled message: " + msg.what);
4703 }
4704 }
4705 }
4706
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004707 private void logRecord(UserAccounts accounts, String action, String tableName) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004708 logRecord(action, tableName, -1, accounts);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004709 }
4710
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004711 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004712 logRecord(action, tableName, -1, accounts, uid);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004713 }
4714
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004715 /*
4716 * This function receives an opened writable database.
4717 */
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004718 private void logRecord(String action, String tableName, long accountId,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004719 UserAccounts userAccount) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004720 logRecord(action, tableName, accountId, userAccount, getCallingUid());
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004721 }
4722
4723 /*
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004724 * This function receives an opened writable database and writes to it in a separate thread.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004725 */
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004726 private void logRecord(String action, String tableName, long accountId,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004727 UserAccounts userAccount, int callingUid) {
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004728
4729 class LogRecordTask implements Runnable {
4730 private final String action;
4731 private final String tableName;
4732 private final long accountId;
4733 private final UserAccounts userAccount;
4734 private final int callingUid;
4735 private final long userDebugDbInsertionPoint;
4736
4737 LogRecordTask(final String action,
4738 final String tableName,
4739 final long accountId,
4740 final UserAccounts userAccount,
4741 final int callingUid,
4742 final long userDebugDbInsertionPoint) {
4743 this.action = action;
4744 this.tableName = tableName;
4745 this.accountId = accountId;
4746 this.userAccount = userAccount;
4747 this.callingUid = callingUid;
4748 this.userDebugDbInsertionPoint = userDebugDbInsertionPoint;
4749 }
4750
4751 public void run() {
4752 SQLiteStatement logStatement = userAccount.statementForLogging;
4753 logStatement.bindLong(1, accountId);
4754 logStatement.bindString(2, action);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004755 logStatement.bindString(3, mDateFormat.format(new Date()));
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004756 logStatement.bindLong(4, callingUid);
4757 logStatement.bindString(5, tableName);
4758 logStatement.bindLong(6, userDebugDbInsertionPoint);
4759 logStatement.execute();
4760 logStatement.clearBindings();
4761 }
4762 }
4763
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004764 LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
4765 callingUid, userAccount.debugDbInsertionPoint);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004766 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004767 % AccountsDb.MAX_DEBUG_DB_SIZE;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004768 mHandler.post(logTask);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004769 }
4770
4771 /*
4772 * This should only be called once to compile the sql statement for logging
4773 * and to find the insertion point.
4774 */
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004775 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(UserAccounts userAccount) {
4776 userAccount.debugDbInsertionPoint = userAccount.accountsDb
4777 .calculateDebugTableInsertionPoint();
4778 userAccount.statementForLogging = userAccount.accountsDb.compileSqlStatementForLogging();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004779 }
4780
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004781 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07004782 return asBinder();
4783 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004784
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004785 /**
4786 * Searches array of arguments for the specified string
4787 * @param args array of argument strings
4788 * @param value value to search for
4789 * @return true if the value is contained in the array
4790 */
4791 private static boolean scanArgs(String[] args, String value) {
4792 if (args != null) {
4793 for (String arg : args) {
4794 if (value.equals(arg)) {
4795 return true;
4796 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004797 }
4798 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004799 return false;
4800 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004801
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004802 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004803 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07004804 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
4805 != PackageManager.PERMISSION_GRANTED) {
4806 fout.println("Permission Denial: can't dump AccountsManager from from pid="
4807 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
4808 + " without permission " + android.Manifest.permission.DUMP);
4809 return;
4810 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004811 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004812 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07004813
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004814 final List<UserInfo> users = getUserManager().getUsers();
4815 for (UserInfo user : users) {
4816 ipw.println("User " + user + ":");
4817 ipw.increaseIndent();
4818 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
4819 ipw.println();
4820 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08004821 }
4822 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004823
Amith Yamasani04e0d262012-02-14 11:50:53 -08004824 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
4825 String[] args, boolean isCheckinRequest) {
4826 synchronized (userAccounts.cacheLock) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004827 if (isCheckinRequest) {
4828 // This is a checkin request. *Only* upload the account types and the count of each.
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004829 userAccounts.accountsDb.dumpDeAccountsTable(fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004830 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004831 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07004832 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004833 fout.println("Accounts: " + accounts.length);
4834 for (Account account : accounts) {
4835 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004836 }
Fred Quintana307da1a2010-01-21 14:24:20 -08004837
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004838 // Add debug information.
4839 fout.println();
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004840 userAccounts.accountsDb.dumpDebugTable(fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004841 fout.println();
4842 synchronized (mSessions) {
4843 final long now = SystemClock.elapsedRealtime();
4844 fout.println("Active Sessions: " + mSessions.size());
4845 for (Session session : mSessions.values()) {
4846 fout.println(" " + session.toDebugString(now));
4847 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004848 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004849
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004850 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004851 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004852 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004853 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004854 }
4855
Amith Yamasani04e0d262012-02-14 11:50:53 -08004856 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004857 Intent intent, String packageName, final int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004858 long identityToken = clearCallingIdentity();
4859 try {
4860 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4861 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
4862 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004863
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004864 if (intent.getComponent() != null &&
4865 GrantCredentialsPermissionActivity.class.getName().equals(
4866 intent.getComponent().getClassName())) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004867 createNoCredentialsPermissionNotification(account, intent, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004868 } else {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004869 Context contextForUser = getContextForUser(new UserHandle(userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08004870 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07004871 intent.addCategory(String.valueOf(notificationId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004872
Fred Quintana33f889a2009-09-14 17:31:26 -07004873 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004874 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04004875 Notification n = new Notification.Builder(contextForUser)
4876 .setWhen(0)
4877 .setSmallIcon(android.R.drawable.stat_sys_warning)
4878 .setColor(contextForUser.getColor(
4879 com.android.internal.R.color.system_notification_accent_color))
4880 .setContentTitle(String.format(notificationTitleFormat, account.name))
4881 .setContentText(message)
4882 .setContentIntent(PendingIntent.getActivityAsUser(
4883 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004884 null, new UserHandle(userId)))
Chris Wren1ce4b6d2015-06-11 10:19:43 -04004885 .build();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004886 installNotification(notificationId, n, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004887 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004888 } finally {
4889 restoreCallingIdentity(identityToken);
4890 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004891 }
4892
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004893 private void installNotification(int notificationId, final Notification notification,
4894 String packageName, int userId) {
4895 final long token = clearCallingIdentity();
4896 try {
Fyodor Kupolovda993802016-09-21 14:47:10 -07004897 INotificationManager notificationManager = mInjector.getNotificationManager();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004898 try {
4899 notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
4900 notificationId, notification, new int[1], userId);
4901 } catch (RemoteException e) {
4902 /* ignore - local call */
4903 }
4904 } finally {
4905 Binder.restoreCallingIdentity(token);
4906 }
Fred Quintana56285a62010-12-02 14:20:51 -08004907 }
4908
Fyodor Kupolovda993802016-09-21 14:47:10 -07004909 private void cancelNotification(int id, UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004910 cancelNotification(id, mContext.getPackageName(), user);
4911 }
4912
Fyodor Kupolovda993802016-09-21 14:47:10 -07004913 private void cancelNotification(int id, String packageName, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004914 long identityToken = clearCallingIdentity();
4915 try {
Fyodor Kupolovda993802016-09-21 14:47:10 -07004916 INotificationManager service = mInjector.getNotificationManager();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004917 service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
4918 } catch (RemoteException e) {
4919 /* ignore - local call */
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004920 } finally {
4921 restoreCallingIdentity(identityToken);
4922 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004923 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004924
Ian Pedowitz358e51f2016-03-15 17:08:27 +00004925 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
4926 for (String perm : permissions) {
4927 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
4928 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4929 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
4930 }
4931 final int opCode = AppOpsManager.permissionToOpCode(perm);
4932 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
4933 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
4934 return true;
4935 }
4936 }
4937 }
4938 return false;
4939 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004940
Amith Yamasani67df64b2012-12-14 12:09:36 -08004941 private int handleIncomingUser(int userId) {
4942 try {
4943 return ActivityManagerNative.getDefault().handleIncomingUser(
4944 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
4945 } catch (RemoteException re) {
4946 // Shouldn't happen, local.
4947 }
4948 return userId;
4949 }
4950
Christopher Tateccbf84f2013-05-08 15:25:41 -07004951 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004952 final int callingUserId = UserHandle.getUserId(callingUid);
4953
4954 final PackageManager userPackageManager;
4955 try {
4956 userPackageManager = mContext.createPackageContextAsUser(
4957 "android", 0, new UserHandle(callingUserId)).getPackageManager();
4958 } catch (NameNotFoundException e) {
4959 return false;
4960 }
4961
4962 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07004963 for (String name : packages) {
4964 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004965 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08004966 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08004967 && (packageInfo.applicationInfo.privateFlags
4968 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07004969 return true;
4970 }
4971 } catch (PackageManager.NameNotFoundException e) {
4972 return false;
4973 }
4974 }
4975 return false;
4976 }
4977
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004978 private boolean permissionIsGranted(
4979 Account account, String authTokenType, int callerUid, int userId) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004980 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
4981 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4982 Log.v(TAG, "Access to " + account + " granted calling uid is system");
4983 }
4984 return true;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004985 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004986
4987 if (isPrivileged(callerUid)) {
4988 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4989 Log.v(TAG, "Access to " + account + " granted calling uid "
4990 + callerUid + " privileged");
4991 }
4992 return true;
4993 }
4994 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
4995 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4996 Log.v(TAG, "Access to " + account + " granted calling uid "
4997 + callerUid + " manages the account");
4998 }
4999 return true;
5000 }
5001 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5002 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5003 Log.v(TAG, "Access to " + account + " granted calling uid "
5004 + callerUid + " user granted access");
5005 }
5006 return true;
5007 }
5008
5009 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5010 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5011 }
5012
5013 return false;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005014 }
5015
Svetoslavf3f02ac2015-09-08 14:36:35 -07005016 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5017 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005018 if (accountType == null) {
5019 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005020 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07005021 return getTypesVisibleToCaller(callingUid, userId,
5022 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005023 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005024 }
5025
5026 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5027 if (accountType == null) {
5028 return false;
5029 } else {
5030 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5031 }
5032 }
5033
Svetoslavf3f02ac2015-09-08 14:36:35 -07005034 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5035 String opPackageName) {
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005036 boolean isPermitted =
5037 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5038 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005039 return getTypesForCaller(callingUid, userId, isPermitted);
5040 }
5041
5042 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5043 return getTypesForCaller(callingUid, userId, false);
5044 }
5045
5046 private List<String> getTypesForCaller(
5047 int callingUid, int userId, boolean isOtherwisePermitted) {
5048 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005049 long identityToken = Binder.clearCallingIdentity();
5050 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5051 try {
5052 serviceInfos = mAuthenticatorCache.getAllServices(userId);
5053 } finally {
5054 Binder.restoreCallingIdentity(identityToken);
5055 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005056 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005057 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005058 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5059 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5060 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005061 }
5062 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005063 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005064 }
5065
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07005066 private boolean isAccountPresentForCaller(String accountName, String accountType) {
5067 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5068 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5069 if (account.name.equals(accountName)) {
5070 return true;
5071 }
5072 }
5073 }
5074 return false;
5075 }
5076
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07005077 private static void checkManageUsersPermission(String message) {
5078 if (ActivityManager.checkComponentPermission(
5079 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5080 != PackageManager.PERMISSION_GRANTED) {
5081 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5082 }
5083 }
5084
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07005085 private static void checkManageOrCreateUsersPermission(String message) {
5086 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5087 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5088 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5089 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5090 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5091 + message);
5092 }
5093 }
5094
Amith Yamasani04e0d262012-02-14 11:50:53 -08005095 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5096 int callerUid) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005097 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005098 return true;
5099 }
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005100 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005101 synchronized (accounts.cacheLock) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005102 long grantsCount;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005103 if (authTokenType != null) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005104 grantsCount = accounts.accountsDb.findMatchingGrantsCount(callerUid, authTokenType,
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005105 account);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005106 } else {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005107 grantsCount = accounts.accountsDb.findMatchingGrantsCountAnyToken(callerUid,
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005108 account);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005109 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005110 final boolean permissionGranted = grantsCount > 0;
Svet Ganov890a2102016-08-24 00:08:00 -07005111
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005112 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5113 // TODO: Skip this check when running automated tests. Replace this
5114 // with a more general solution.
5115 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08005116 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005117 + " but ignoring since device is in test harness.");
5118 return true;
5119 }
5120 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005121 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005122 }
5123
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005124 private boolean isSystemUid(int callingUid) {
5125 String[] packages = null;
5126 long ident = Binder.clearCallingIdentity();
5127 try {
5128 packages = mPackageManager.getPackagesForUid(callingUid);
5129 } finally {
5130 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005131 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005132 if (packages != null) {
5133 for (String name : packages) {
5134 try {
5135 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5136 if (packageInfo != null
5137 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5138 != 0) {
5139 return true;
5140 }
5141 } catch (PackageManager.NameNotFoundException e) {
5142 Log.w(TAG, String.format("Could not find package [%s]", name), e);
5143 }
5144 }
5145 } else {
5146 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005147 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005148 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00005149 }
5150
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005151 /** Succeeds if any of the specified permissions are granted. */
5152 private void checkReadAccountsPermitted(
5153 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005154 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07005155 int userId,
5156 String opPackageName) {
5157 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005158 String msg = String.format(
5159 "caller uid %s cannot access %s accounts",
5160 callingUid,
5161 accountType);
5162 Log.w(TAG, " " + msg);
5163 throw new SecurityException(msg);
5164 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005165 }
5166
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005167 private boolean canUserModifyAccounts(int userId, int callingUid) {
5168 // the managing app can always modify accounts
5169 if (isProfileOwner(callingUid)) {
5170 return true;
5171 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005172 if (getUserManager().getUserRestrictions(new UserHandle(userId))
5173 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5174 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005175 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005176 return true;
5177 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005178
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005179 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5180 // the managing app can always modify accounts
5181 if (isProfileOwner(callingUid)) {
5182 return true;
5183 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005184 DevicePolicyManager dpm = (DevicePolicyManager) mContext
5185 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005186 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02005187 if (typesArray == null) {
5188 return true;
5189 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005190 for (String forbiddenType : typesArray) {
5191 if (forbiddenType.equals(accountType)) {
5192 return false;
5193 }
5194 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005195 return true;
5196 }
5197
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005198 private boolean isProfileOwner(int uid) {
5199 final DevicePolicyManagerInternal dpmi =
5200 LocalServices.getService(DevicePolicyManagerInternal.class);
5201 return (dpmi != null)
5202 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5203 }
5204
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08005205 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07005206 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5207 throws RemoteException {
5208 final int callingUid = getCallingUid();
5209
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005210 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07005211 throw new SecurityException();
5212 }
5213
5214 if (value) {
5215 grantAppPermission(account, authTokenType, uid);
5216 } else {
5217 revokeAppPermission(account, authTokenType, uid);
5218 }
5219 }
5220
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005221 /**
5222 * Allow callers with the given uid permission to get credentials for account/authTokenType.
5223 * <p>
5224 * Although this is public it can only be accessed via the AccountManagerService object
5225 * which is in the system. This means we don't need to protect it with permissions.
5226 * @hide
5227 */
Svet Ganov5d09c992016-09-07 09:57:41 -07005228 void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005229 if (account == null || authTokenType == null) {
5230 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005231 return;
5232 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005233 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005234 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005235 long accountId = accounts.accountsDb.findDeAccountId(account);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005236 if (accountId >= 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005237 accounts.accountsDb.insertGrant(accountId, authTokenType, uid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005238 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005239 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005240 UserHandle.of(accounts.userId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005241
5242 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005243 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005244
5245 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5246 for (AccountManagerInternal.OnAppPermissionChangeListener listener
5247 : mAppPermissionChangeListeners) {
5248 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5249 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005250 }
5251
5252 /**
5253 * Don't allow callers with the given uid permission to get credentials for
5254 * account/authTokenType.
5255 * <p>
5256 * Although this is public it can only be accessed via the AccountManagerService object
5257 * which is in the system. This means we don't need to protect it with permissions.
5258 * @hide
5259 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005260 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005261 if (account == null || authTokenType == null) {
5262 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005263 return;
5264 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005265 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005266 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005267 accounts.accountsDb.beginTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005268 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005269 long accountId = accounts.accountsDb.findDeAccountId(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005270 if (accountId >= 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005271 accounts.accountsDb.deleteGrantsByAccountIdAuthTokenTypeAndUid(
5272 accountId, authTokenType, uid);
5273 accounts.accountsDb.setTransactionSuccessful();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005274 }
5275 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005276 accounts.accountsDb.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005277 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005278
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005279 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5280 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005281 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005282
5283 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5284 for (AccountManagerInternal.OnAppPermissionChangeListener listener
5285 : mAppPermissionChangeListeners) {
5286 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5287 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005288 }
Fred Quintana56285a62010-12-02 14:20:51 -08005289
Amith Yamasani04e0d262012-02-14 11:50:53 -08005290 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
5291 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005292 if (oldAccountsForType != null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005293 ArrayList<Account> newAccountsList = new ArrayList<>();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005294 for (Account curAccount : oldAccountsForType) {
5295 if (!curAccount.equals(account)) {
5296 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005297 }
5298 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005299 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005300 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005301 } else {
5302 Account[] newAccountsForType = new Account[newAccountsList.size()];
5303 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005304 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005305 }
Fred Quintana56285a62010-12-02 14:20:51 -08005306 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005307 accounts.userDataCache.remove(account);
5308 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005309 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08005310 }
5311
5312 /**
5313 * This assumes that the caller has already checked that the account is not already present.
Svetoslav Ganov57f62592016-09-16 17:29:05 -07005314 * IMPORTANT: The account being inserted will begin to be tracked for access in remote
5315 * processes and if you will return this account to apps you should return the result.
5316 * @return The inserted account which is a new instance that is being tracked.
Fred Quintana56285a62010-12-02 14:20:51 -08005317 */
Svetoslav Ganov57f62592016-09-16 17:29:05 -07005318 private Account insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005319 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005320 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
5321 Account[] newAccountsForType = new Account[oldLength + 1];
5322 if (accountsForType != null) {
5323 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08005324 }
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07005325 String token = account.getAccessId() != null ? account.getAccessId()
5326 : UUID.randomUUID().toString();
5327 newAccountsForType[oldLength] = new Account(account, token);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005328 accounts.accountCache.put(account.type, newAccountsForType);
Svetoslav Ganov57f62592016-09-16 17:29:05 -07005329 return newAccountsForType[oldLength];
Fred Quintana56285a62010-12-02 14:20:51 -08005330 }
5331
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005332 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07005333 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005334 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07005335 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005336 return unfiltered;
5337 }
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07005338 UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
Amith Yamasani0c19bf52013-10-03 10:34:58 -07005339 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005340 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005341 // If any of the packages is a visible listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005342 // otherwise return non-shared accounts only.
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005343 // This might be a temporary way to specify a visible list
5344 String visibleList = mContext.getResources().getString(
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005345 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
5346 for (String packageName : packages) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005347 if (visibleList.contains(";" + packageName + ";")) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005348 return unfiltered;
5349 }
5350 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005351 ArrayList<Account> allowed = new ArrayList<>();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005352 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
5353 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005354 String requiredAccountType = "";
5355 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07005356 // If there's an explicit callingPackage specified, check if that package
5357 // opted in to see restricted accounts.
5358 if (callingPackage != null) {
5359 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005360 if (pi != null && pi.restrictedAccountType != null) {
5361 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07005362 }
5363 } else {
5364 // Otherwise check if the callingUid has a package that has opted in
5365 for (String packageName : packages) {
5366 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
5367 if (pi != null && pi.restrictedAccountType != null) {
5368 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07005369 break;
5370 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005371 }
5372 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005373 } catch (NameNotFoundException nnfe) {
5374 }
5375 for (Account account : unfiltered) {
5376 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005377 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005378 } else {
5379 boolean found = false;
5380 for (Account shared : sharedAccounts) {
5381 if (shared.equals(account)) {
5382 found = true;
5383 break;
5384 }
5385 }
5386 if (!found) {
5387 allowed.add(account);
5388 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005389 }
5390 }
5391 Account[] filtered = new Account[allowed.size()];
5392 allowed.toArray(filtered);
5393 return filtered;
5394 } else {
5395 return unfiltered;
5396 }
5397 }
5398
Amith Yamasani27db4682013-03-30 17:07:47 -07005399 /*
5400 * packageName can be null. If not null, it should be used to filter out restricted accounts
5401 * that the package is not allowed to access.
5402 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005403 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07005404 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005405 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005406 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005407 if (accounts == null) {
5408 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08005409 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005410 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07005411 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08005412 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005413 } else {
5414 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08005415 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005416 totalLength += accounts.length;
5417 }
5418 if (totalLength == 0) {
5419 return EMPTY_ACCOUNT_ARRAY;
5420 }
5421 Account[] accounts = new Account[totalLength];
5422 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08005423 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005424 System.arraycopy(accountsOfType, 0, accounts, totalLength,
5425 accountsOfType.length);
5426 totalLength += accountsOfType.length;
5427 }
Amith Yamasani27db4682013-03-30 17:07:47 -07005428 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08005429 }
5430 }
5431
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005432 protected void writeUserDataIntoCacheLocked(UserAccounts accounts,
Amith Yamasani04e0d262012-02-14 11:50:53 -08005433 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005434 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005435 if (userDataForAccount == null) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005436 userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005437 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005438 }
5439 if (value == null) {
5440 userDataForAccount.remove(key);
5441 } else {
5442 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08005443 }
5444 }
5445
Carlos Valdivia91979be2015-05-22 14:11:35 -07005446 protected String readCachedTokenInternal(
5447 UserAccounts accounts,
5448 Account account,
5449 String tokenType,
5450 String callingPackage,
5451 byte[] pkgSigDigest) {
5452 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005453 return accounts.accountTokenCaches.get(
5454 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07005455 }
5456 }
5457
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005458 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts,
Amith Yamasani04e0d262012-02-14 11:50:53 -08005459 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005460 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005461 if (authTokensForAccount == null) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005462 authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005463 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005464 }
5465 if (value == null) {
5466 authTokensForAccount.remove(key);
5467 } else {
5468 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08005469 }
5470 }
5471
Amith Yamasani04e0d262012-02-14 11:50:53 -08005472 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
5473 String authTokenType) {
5474 synchronized (accounts.cacheLock) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005475 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08005476 if (authTokensForAccount == null) {
5477 // need to populate the cache for this account
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005478 authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005479 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005480 }
5481 return authTokensForAccount.get(authTokenType);
5482 }
5483 }
5484
Simranjit Kohli858511c2016-03-10 18:36:11 +00005485 protected String readUserDataInternalLocked(
5486 UserAccounts accounts, Account account, String key) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005487 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00005488 if (userDataForAccount == null) {
5489 // need to populate the cache for this account
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005490 userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00005491 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005492 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00005493 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08005494 }
5495
Kenny Guy07ad8dc2014-09-01 20:56:12 +01005496 private Context getContextForUser(UserHandle user) {
5497 try {
5498 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
5499 } catch (NameNotFoundException e) {
5500 // Default to mContext, not finding the package system is running as is unlikely.
5501 return mContext;
5502 }
5503 }
Sandra Kwan78812282015-11-04 11:19:47 -08005504
5505 private void sendResponse(IAccountManagerResponse response, Bundle result) {
5506 try {
5507 response.onResult(result);
5508 } catch (RemoteException e) {
5509 // if the caller is dead then there is no one to care about remote
5510 // exceptions
5511 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5512 Log.v(TAG, "failure while notifying response", e);
5513 }
5514 }
5515 }
5516
5517 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
5518 String errorMessage) {
5519 try {
5520 response.onError(errorCode, errorMessage);
5521 } catch (RemoteException e) {
5522 // if the caller is dead then there is no one to care about remote
5523 // exceptions
5524 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5525 Log.v(TAG, "failure while notifying response", e);
5526 }
5527 }
5528 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005529
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005530 private final class AccountManagerInternalImpl extends AccountManagerInternal {
Svet Ganov5d09c992016-09-07 09:57:41 -07005531 private final Object mLock = new Object();
5532
5533 @GuardedBy("mLock")
5534 private AccountManagerBackupHelper mBackupHelper;
5535
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005536 @Override
5537 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
5538 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
5539 if (account == null) {
5540 Slog.w(TAG, "account cannot be null");
5541 return;
5542 }
5543 if (packageName == null) {
5544 Slog.w(TAG, "packageName cannot be null");
5545 return;
5546 }
5547 if (userId < UserHandle.USER_SYSTEM) {
5548 Slog.w(TAG, "user id must be concrete");
5549 return;
5550 }
5551 if (callback == null) {
5552 Slog.w(TAG, "callback cannot be null");
5553 return;
5554 }
5555
Svet Ganovf6d424f12016-09-20 20:18:53 -07005556 if (AccountManagerService.this.hasAccountAccess(account, packageName,
5557 new UserHandle(userId))) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005558 Bundle result = new Bundle();
5559 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
5560 callback.sendResult(result);
5561 return;
5562 }
5563
5564 final int uid;
5565 try {
5566 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
5567 } catch (NameNotFoundException e) {
5568 Slog.e(TAG, "Unknown package " + packageName);
5569 return;
5570 }
5571
5572 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
Svet Ganovf6d424f12016-09-20 20:18:53 -07005573 final UserAccounts userAccounts;
5574 synchronized (mUsers) {
5575 userAccounts = mUsers.get(userId);
5576 }
5577 doNotification(userAccounts, account, null, intent, packageName, userId);
5578 }
5579
5580 @Override
5581 public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) {
5582 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5583 mAppPermissionChangeListeners.add(listener);
5584 }
5585
5586 @Override
5587 public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) {
5588 return AccountManagerService.this.hasAccountAccess(account, null, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005589 }
Svet Ganov5d09c992016-09-07 09:57:41 -07005590
5591 @Override
5592 public byte[] backupAccountAccessPermissions(int userId) {
5593 synchronized (mLock) {
5594 if (mBackupHelper == null) {
5595 mBackupHelper = new AccountManagerBackupHelper(
5596 AccountManagerService.this, this);
5597 }
5598 return mBackupHelper.backupAccountAccessPermissions(userId);
5599 }
5600 }
5601
5602 @Override
5603 public void restoreAccountAccessPermissions(byte[] data, int userId) {
5604 synchronized (mLock) {
5605 if (mBackupHelper == null) {
5606 mBackupHelper = new AccountManagerBackupHelper(
5607 AccountManagerService.this, this);
5608 }
5609 mBackupHelper.restoreAccountAccessPermissions(data, userId);
5610 }
5611 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005612 }
Fyodor Kupolovda993802016-09-21 14:47:10 -07005613
5614 @VisibleForTesting
5615 static class Injector {
5616 private final Context mContext;
5617
5618 public Injector(Context context) {
5619 mContext = context;
5620 }
5621
5622 Looper getMessageHandlerLooper() {
5623 ServiceThread serviceThread = new ServiceThread(TAG,
5624 android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
5625 serviceThread.start();
5626 return serviceThread.getLooper();
5627 }
5628
5629 Context getContext() {
5630 return mContext;
5631 }
5632
5633 void addLocalService(AccountManagerInternal service) {
5634 LocalServices.addService(AccountManagerInternal.class, service);
5635 }
5636
5637 String getDeDatabaseName(int userId) {
5638 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
5639 AccountsDb.DE_DATABASE_NAME);
5640 return databaseFile.getPath();
5641 }
5642
5643 String getCeDatabaseName(int userId) {
5644 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
5645 AccountsDb.CE_DATABASE_NAME);
5646 return databaseFile.getPath();
5647 }
5648
5649 String getPreNDatabaseName(int userId) {
5650 File systemDir = Environment.getDataSystemDirectory();
5651 File databaseFile = new File(Environment.getUserSystemDirectory(userId),
5652 PRE_N_DATABASE_NAME);
5653 if (userId == 0) {
5654 // Migrate old file, if it exists, to the new location.
5655 // Make sure the new file doesn't already exist. A dummy file could have been
5656 // accidentally created in the old location, causing the new one to become corrupted
5657 // as well.
5658 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
5659 if (oldFile.exists() && !databaseFile.exists()) {
5660 // Check for use directory; create if it doesn't exist, else renameTo will fail
5661 File userDir = Environment.getUserSystemDirectory(userId);
5662 if (!userDir.exists()) {
5663 if (!userDir.mkdirs()) {
5664 throw new IllegalStateException("User dir cannot be created: " + userDir);
5665 }
5666 }
5667 if (!oldFile.renameTo(databaseFile)) {
5668 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
5669 }
5670 }
5671 }
5672 return databaseFile.getPath();
5673 }
5674
5675 IAccountAuthenticatorCache getAccountAuthenticatorCache() {
5676 return new AccountAuthenticatorCache(mContext);
5677 }
5678
5679 INotificationManager getNotificationManager() {
5680 return NotificationManager.getService();
5681 }
5682 }
Fred Quintana60307342009-03-24 22:48:12 -07005683}