blob: b5fecfb17aeb68a2383d626bc3ed0ef9cf063c94 [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;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070037import android.app.ActivityThread;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070038import android.app.AppGlobals;
Svetoslavf3f02ac2015-09-08 14:36:35 -070039import android.app.AppOpsManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070040import android.app.INotificationManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070041import android.app.Notification;
42import android.app.NotificationManager;
43import android.app.PendingIntent;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000044import android.app.admin.DeviceAdminInfo;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010045import android.app.admin.DevicePolicyManager;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000046import android.app.admin.DevicePolicyManagerInternal;
Fred Quintanaa698f422009-04-08 19:14:54 -070047import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070048import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070049import android.content.Context;
50import android.content.Intent;
51import android.content.IntentFilter;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070052import android.content.IntentSender;
Fred Quintanab839afc2009-10-14 15:57:28 -070053import android.content.ServiceConnection;
Carlos Valdivia6ede9c32016-03-10 20:12:32 -080054import android.content.pm.ActivityInfo;
Doug Zongker885cfc232009-10-21 16:52:44 -070055import android.content.pm.ApplicationInfo;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070056import android.content.pm.IPackageManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070057import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070058import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070059import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070060import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070061import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070062import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070063import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070064import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070065import android.database.Cursor;
Fred Quintanaa698f422009-04-08 19:14:54 -070066import android.database.sqlite.SQLiteDatabase;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070067import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070068import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070069import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080070import android.os.Environment;
Fred Quintanaa698f422009-04-08 19:14:54 -070071import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070072import android.os.IBinder;
73import android.os.Looper;
74import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070075import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070076import android.os.Process;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070077import android.os.RemoteCallback;
Fred Quintanaa698f422009-04-08 19:14:54 -070078import android.os.RemoteException;
79import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070080import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070081import android.os.UserManager;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070082import android.os.storage.StorageManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070083import android.text.TextUtils;
84import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070085import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070086import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080087import android.util.SparseArray;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070088import android.util.SparseBooleanArray;
Fred Quintana60307342009-03-24 22:48:12 -070089
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070090import com.android.internal.R;
Svet Ganov5d09c992016-09-07 09:57:41 -070091import com.android.internal.annotations.GuardedBy;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -070092import com.android.internal.annotations.VisibleForTesting;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070093import com.android.internal.content.PackageMonitor;
Amith Yamasani67df64b2012-12-14 12:09:36 -080094import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -080095import com.android.internal.util.IndentingPrintWriter;
Fyodor Kupolov35f68082016-04-06 12:14:17 -070096import com.android.internal.util.Preconditions;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000097import com.android.server.LocalServices;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -070098import com.android.server.ServiceThread;
Jeff Sharkey1cab76a2016-04-12 18:23:31 -060099import com.android.server.SystemService;
100
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700101import com.google.android.collect.Lists;
102import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700103
Oscar Montemayora8529f62009-11-18 10:14:20 -0800104import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -0700105import java.io.FileDescriptor;
106import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -0800107import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -0700108import java.security.MessageDigest;
109import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700110import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -0700111import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -0800112import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700113import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700114import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700115import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700116import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -0800117import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700118import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800119import java.util.Map;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800120import java.util.Map.Entry;
Svet Ganovc1c0d1c2016-09-23 19:15:47 -0700121import java.util.Objects;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700122import java.util.Set;
Svet Ganovc1c0d1c2016-09-23 19:15:47 -0700123import java.util.UUID;
Svet Ganovf6d424f12016-09-20 20:18:53 -0700124import java.util.concurrent.CopyOnWriteArrayList;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700125import java.util.concurrent.atomic.AtomicInteger;
126import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700127
Fred Quintana60307342009-03-24 22:48:12 -0700128/**
129 * A system service that provides account, password, and authtoken management for all
130 * accounts on the device. Some of these calls are implemented with the help of the corresponding
131 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
132 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700133 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700134 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700135 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700136public class AccountManagerService
137 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800138 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -0700139 private static final String TAG = "AccountManagerService";
140
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600141 public static class Lifecycle extends SystemService {
142 private AccountManagerService mService;
143
144 public Lifecycle(Context context) {
145 super(context);
146 }
147
148 @Override
149 public void onStart() {
Fyodor Kupolovda993802016-09-21 14:47:10 -0700150 mService = new AccountManagerService(new Injector(getContext()));
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600151 publishBinderService(Context.ACCOUNT_SERVICE, mService);
152 }
153
154 @Override
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600155 public void onUnlockUser(int userHandle) {
156 mService.onUnlockUser(userHandle);
157 }
158 }
159
Svet Ganov5d09c992016-09-07 09:57:41 -0700160 final Context mContext;
Fred Quintana60307342009-03-24 22:48:12 -0700161
Fred Quintana56285a62010-12-02 14:20:51 -0800162 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700163 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700164 private UserManager mUserManager;
Fyodor Kupolovda993802016-09-21 14:47:10 -0700165 private final Injector mInjector;
Fred Quintana56285a62010-12-02 14:20:51 -0800166
Svet Ganov5d09c992016-09-07 09:57:41 -0700167 final MessageHandler mHandler;
Tejas Khorana7b88f0e2016-06-13 13:06:35 -0700168
Fred Quintana60307342009-03-24 22:48:12 -0700169 // Messages that can be sent on mHandler
170 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700171 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700172
Fred Quintana56285a62010-12-02 14:20:51 -0800173 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700174 private static final String PRE_N_DATABASE_NAME = "accounts.db";
Fred Quintana7be59642009-08-24 18:29:25 -0700175 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800176
Carlos Valdivia91979be2015-05-22 14:11:35 -0700177 static {
178 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
179 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
180 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700181
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800182
Fred Quintanaa698f422009-04-08 19:14:54 -0700183 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700184 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
185
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700186 private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
187
Amith Yamasani04e0d262012-02-14 11:50:53 -0800188 static class UserAccounts {
189 private final int userId;
Fyodor Kupolov00de49e2016-09-23 13:10:27 -0700190 final AccountsDb accountsDb;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800191 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
192 credentialsPermissionNotificationIds =
193 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
194 private final HashMap<Account, Integer> signinRequiredNotificationIds =
195 new HashMap<Account, Integer>();
Svet Ganov5d09c992016-09-07 09:57:41 -0700196 final Object cacheLock = new Object();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800197 /** protected by the {@link #cacheLock} */
Svet Ganov5d09c992016-09-07 09:57:41 -0700198 final HashMap<String, Account[]> accountCache =
Svet Ganovf6d424f12016-09-20 20:18:53 -0700199 new LinkedHashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800200 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700201 private final Map<Account, Map<String, String>> userDataCache = new HashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800202 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700203 private final Map<Account, Map<String, String>> authTokenCache = new HashMap<>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700204
205 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700206 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700207
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700208 /** protected by the {@link #cacheLock} */
209 private final Map<String, ArrayList<Integer>> mApplicationAccountRequestMappings =
210 new HashMap<>();
211
212 /* Together the below two Sparse Arrays serve as visible list. One maps UID to account
213 number. Another maps Account number to Account.*/
214
215 /** protected by the {@link #cacheLock} */
216 private final SparseArray<ArrayList<Integer>> mVisibleListUidToMockAccountNumbers =
217 new SparseArray<>();
218
219 //TODO: Instead of using Mock Account IDs, use the actual account IDs.
220 /** protected by the {@link #cacheLock} */
221 private final SparseArray<Account> mMockAccountIdToAccount = new SparseArray<>();
222
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700223 /**
224 * protected by the {@link #cacheLock}
225 *
226 * Caches the previous names associated with an account. Previous names
227 * should be cached because we expect that when an Account is renamed,
228 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
229 * want to know if the accounts they care about have been renamed.
230 *
231 * The previous names are wrapped in an {@link AtomicReference} so that
232 * we can distinguish between those accounts with no previous names and
233 * those whose previous names haven't been cached (yet).
234 */
235 private final HashMap<Account, AtomicReference<String>> previousNameCache =
236 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800237
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700238 private int debugDbInsertionPoint = -1;
Fyodor Kupolov00de49e2016-09-23 13:10:27 -0700239 private SQLiteStatement statementForLogging; // TODO Move to AccountsDb
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700240
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700241 UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800242 this.userId = userId;
243 synchronized (cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -0700244 accountsDb = AccountsDb.create(context, userId, preNDbFile, deDbFile);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800245 }
246 }
247 }
248
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700249 private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600250 private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -0700251 // Not thread-safe. Only use in synchronized context
252 private final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Svet Ganovf6d424f12016-09-20 20:18:53 -0700253 private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
254 mAppPermissionChangeListeners = new CopyOnWriteArrayList<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800255
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700256 private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
Fred Quintana31957f12009-10-21 13:43:10 -0700257 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700258
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700259 /**
260 * This should only be called by system code. One should only call this after the service
261 * has started.
262 * @return a reference to the AccountManagerService instance
263 * @hide
264 */
265 public static AccountManagerService getSingleton() {
266 return sThis.get();
267 }
Fred Quintana60307342009-03-24 22:48:12 -0700268
Fyodor Kupolovda993802016-09-21 14:47:10 -0700269 public AccountManagerService(Injector injector) {
270 mInjector = injector;
271 mContext = injector.getContext();
272 mPackageManager = mContext.getPackageManager();
Svetoslavf3f02ac2015-09-08 14:36:35 -0700273 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fyodor Kupolovda993802016-09-21 14:47:10 -0700274 mHandler = new MessageHandler(injector.getMessageHandlerLooper());
275 mAuthenticatorCache = mInjector.getAccountAuthenticatorCache();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800276 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700277
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700278 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800279
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700280 addRequestsForPreInstalledApplications();
281
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800282 IntentFilter intentFilter = new IntentFilter();
283 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
284 intentFilter.addDataScheme("package");
285 mContext.registerReceiver(new BroadcastReceiver() {
286 @Override
287 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700288 // Don't delete accounts when updating a authenticator's
289 // package.
290 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700291 /* Purging data requires file io, don't block the main thread. This is probably
292 * less than ideal because we are introducing a race condition where old grants
293 * could be exercised until they are purged. But that race condition existed
294 * anyway with the broadcast receiver.
295 *
296 * Ideally, we would completely clear the cache, purge data from the database,
297 * and then rebuild the cache. All under the cache lock. But that change is too
298 * large at this point.
299 */
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700300 Runnable purgingRunnable = new Runnable() {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700301 @Override
302 public void run() {
303 purgeOldGrantsAll();
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700304
305 /* clears application request's for account types supported */
306 int uidOfUninstalledApplication =
307 intent.getIntExtra(Intent.EXTRA_UID, -1);
308 if(uidOfUninstalledApplication != -1) {
309 clearRequestedAccountVisibility(uidOfUninstalledApplication,
310 getUserAccounts(UserHandle.getUserId(
311 uidOfUninstalledApplication)));
312 }
313
314 /* removes visibility of previous UID of this uninstalled application*/
315 removeAccountVisibilityAllAccounts(uidOfUninstalledApplication,
316 getUserAccounts(UserHandle.getUserId(
317 uidOfUninstalledApplication)));
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700318 }
319 };
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700320 mHandler.post(purgingRunnable);
Carlos Valdivia23f58262014-09-05 10:52:41 -0700321 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700322
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800323 }
324 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800325
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700326 IntentFilter packageAddedOrChangedFilter = new IntentFilter();
327 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
328 packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
329 packageAddedOrChangedFilter.addDataScheme("package");
330 mContext.registerReceiverAsUser(new BroadcastReceiver() {
331 @Override
332 public void onReceive(Context context1, Intent intent) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700333 mHandler.post(new Runnable() {
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700334 @Override
335 public void run() {
336 int uidOfInstalledApplication =
337 intent.getIntExtra(Intent.EXTRA_UID, -1);
338 if(uidOfInstalledApplication != -1) {
339 registerAccountTypesSupported(
340 uidOfInstalledApplication,
341 getUserAccounts(
342 UserHandle.getUserId(uidOfInstalledApplication)));
343 }
344 }
345 });
346 }
347 }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
348
Amith Yamasani13593602012-03-22 16:16:17 -0700349 IntentFilter userFilter = new IntentFilter();
350 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800351 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700352 @Override
353 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800354 String action = intent.getAction();
355 if (Intent.ACTION_USER_REMOVED.equals(action)) {
356 onUserRemoved(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800357 }
Amith Yamasani13593602012-03-22 16:16:17 -0700358 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800359 }, UserHandle.ALL, userFilter, null, null);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700360
Fyodor Kupolovda993802016-09-21 14:47:10 -0700361 injector.addLocalService(new AccountManagerInternalImpl());
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700362
363 // Need to cancel account request notifications if the update/install can access the account
364 new PackageMonitor() {
365 @Override
366 public void onPackageAdded(String packageName, int uid) {
367 // Called on a handler, and running as the system
368 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
369 }
370
371 @Override
372 public void onPackageUpdateFinished(String packageName, int uid) {
373 // Called on a handler, and running as the system
374 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
375 }
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700376 }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700377
378 // Cancel account request notification if an app op was preventing the account access
379 mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
380 new AppOpsManager.OnOpChangedInternalListener() {
381 @Override
382 public void onOpChanged(int op, String packageName) {
383 try {
384 final int userId = ActivityManager.getCurrentUser();
385 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
386 final int mode = mAppOpsManager.checkOpNoThrow(
387 AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
388 if (mode == AppOpsManager.MODE_ALLOWED) {
389 final long identity = Binder.clearCallingIdentity();
390 try {
391 cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
392 } finally {
393 Binder.restoreCallingIdentity(identity);
394 }
395 }
396 } catch (NameNotFoundException e) {
397 /* ignore */
398 }
399 }
400 });
401
402 // Cancel account request notification if a permission was preventing the account access
403 mPackageManager.addOnPermissionsChangeListener(
404 (int uid) -> {
405 Account[] accounts = null;
406 String[] packageNames = mPackageManager.getPackagesForUid(uid);
407 if (packageNames != null) {
408 final int userId = UserHandle.getUserId(uid);
409 final long identity = Binder.clearCallingIdentity();
410 try {
411 for (String packageName : packageNames) {
Fyodor Kupolovda993802016-09-21 14:47:10 -0700412 if (mPackageManager.checkPermission(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700413 Manifest.permission.GET_ACCOUNTS, packageName)
414 != PackageManager.PERMISSION_GRANTED) {
415 continue;
416 }
417
418 if (accounts == null) {
419 accounts = getAccountsAsUser(null, userId, "android");
420 if (ArrayUtils.isEmpty(accounts)) {
421 return;
422 }
423 }
424
425 for (Account account : accounts) {
426 cancelAccountAccessRequestNotificationIfNeeded(
427 account, uid, packageName, true);
428 }
429 }
430 } finally {
431 Binder.restoreCallingIdentity(identity);
432 }
433 }
434 });
435 }
436
437 private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
438 boolean checkAccess) {
439 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
440 for (Account account : accounts) {
441 cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
442 }
443 }
444
445 private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
446 boolean checkAccess) {
447 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
448 for (Account account : accounts) {
449 cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
450 }
451 }
452
453 private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
454 boolean checkAccess) {
455 String[] packageNames = mPackageManager.getPackagesForUid(uid);
456 if (packageNames != null) {
457 for (String packageName : packageNames) {
458 cancelAccountAccessRequestNotificationIfNeeded(account, uid,
459 packageName, checkAccess);
460 }
461 }
462 }
463
464 private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
465 int uid, String packageName, boolean checkAccess) {
466 if (!checkAccess || hasAccountAccess(account, packageName,
467 UserHandle.getUserHandleForUid(uid))) {
468 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -0700469 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700470 UserHandle.getUserHandleForUid(uid));
471 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800472 }
473
Dianne Hackborn164371f2013-10-01 19:10:13 -0700474 @Override
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700475 public boolean addAccountExplicitlyWithUid(Account account, String password, Bundle extras,
476 int[] selectedUids) {
477 if(addAccountExplicitly(account,password,extras)) {
478 for(int thisUid : selectedUids) {
479 makeAccountVisible(account, thisUid);
480 }
481 return true;
482 }
483 return false;
484 }
485
486 @Override
487 public int[] getRequestingUidsForType(String accountType) {
488 int callingUid = Binder.getCallingUid();
489 if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
490 String msg = String.format(
491 "uid %s cannot get secrets for accounts of type: %s",
492 callingUid,
493 accountType);
494 throw new SecurityException(msg);
495 }
496 return getRequestingUidsForType(accountType, getUserAccounts(
497 UserHandle.getUserId(callingUid)));
498 }
499
500 /**
501 * Returns all UIDs for applications that requested the account type. This method
502 * is called indirectly by the Authenticator and AccountManager
503 *
504 * @param accountType authenticator would like to know the requesting apps of
505 * @param ua UserAccount that currently hosts the account and application
506 *
507 * @return ArrayList of all UIDs that support accounts of this
508 * account type that seek approval (to be used to know which accounts for
509 * the authenticator to include in addAccountExplicitly). Null if none.
510 */
511 private int[] getRequestingUidsForType(String accountType, UserAccounts ua) {
512 synchronized(ua.cacheLock) {
513 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
514 ua.mApplicationAccountRequestMappings;
515 ArrayList<Integer> allUidsForAccountType = userApplicationAccountRequestMappings.get(
516 accountType);
517 if(allUidsForAccountType == null) {
518 return null;
519 }
520 int[] toReturn = new int[allUidsForAccountType.size()];
521 for(int i = 0 ; i < toReturn.length ; i++) {
522 toReturn[i] = allUidsForAccountType.get(i);
523 }
524 return toReturn;
525 }
526 }
527
528 @Override
529 public boolean isAccountVisible(Account a, int uid) {
530 int callingUid = Binder.getCallingUid();
531 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
532 String msg = String.format(
533 "uid %s cannot get secrets for accounts of type: %s",
534 callingUid,
535 a.type);
536 throw new SecurityException(msg);
537 }
538 return isAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
539 }
540
541 /**
542 * Checks visibility of certain account of a process identified
543 * by a given UID. This is called by the Authenticator indirectly.
544 *
545 * @param a The account to check visibility of
546 * @param uid UID to check visibility of
547 * @param ua UserAccount that currently hosts the account and application
548 *
549 * @return True if application has access to the account
550 *
551 */
552 private boolean isAccountVisible(Account a, int uid, UserAccounts ua) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700553 if(isAccountManagedByCaller(a.type, uid, UserHandle.getUserId(uid))) {
554 return true;
555 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700556 int accountMapping = getMockAccountNumber(a, ua);
557 if(accountMapping < 0) {
558 return true;
559 }
560 synchronized(ua.cacheLock) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700561 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700562 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
563 ua.mVisibleListUidToMockAccountNumbers;
564 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
Tejas Khorana69990d92016-08-03 11:19:40 -0700565 int indexOfAccountMapping = userAcctIdToAcctMap.indexOfValueByValue(a);
566 return indexOfAccountMapping == -1 || (linkedAccountsToUid != null
567 && linkedAccountsToUid.contains(accountMapping));
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700568 }
569 }
570
571 @Override
572 public boolean makeAccountVisible(Account a, int uid) {
573 int callingUid = Binder.getCallingUid();
574 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
575 String msg = String.format(
576 "uid %s cannot get secrets for accounts of type: %s",
577 callingUid,
578 a.type);
579 throw new SecurityException(msg);
580 }
581 return makeAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
582 }
583
584 /**
585 * Gives a certain UID, represented a application, access to an account. This method
586 * is called indirectly by the Authenticator.
587 *
588 * @param a Account to make visible
589 * @param uid to add visibility of the Account from
590 * @param ua UserAccount that currently hosts the account and application
591 *
592 * @return True if account made visible to application and was not previously visible.
593 */
594 private boolean makeAccountVisible(Account a, int uid, UserAccounts ua) {
595 int accountMapping = getMockAccountNumber(a, ua);
596 if(accountMapping < 0) {
597 accountMapping = makeAccountNumber(a, ua);
598 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700599 synchronized(ua.cacheLock) {
600 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
601 ua.mVisibleListUidToMockAccountNumbers;
602 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
603 if(linkedAccountsToUid == null) {
604 linkedAccountsToUid = new ArrayList<>();
605 linkedAccountsToUid.add(accountMapping);
606 userWlUidToMockAccountNums.put(uid, linkedAccountsToUid);
607 } else if(!linkedAccountsToUid.contains(accountMapping)) {
608 linkedAccountsToUid.add(accountMapping);
609 } else {
610 return false;
611 }
612 }
613
614 String[] subPackages = mPackageManager.getPackagesForUid(uid);
615 if(subPackages != null) {
616 for(String subPackage : subPackages) {
617 sendNotification(subPackage, a);
618 }
619 }
620 return true;
621 }
622
623 @Override
624 public boolean removeAccountVisibility(Account a, int uid) {
625 int callingUid = Binder.getCallingUid();
626 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
627 String msg = String.format(
628 "uid %s cannot get secrets for accounts of type: %s",
629 callingUid,
630 a.type);
631 throw new SecurityException(msg);
632 }
633 return removeAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
634 }
635
636 /**
637 * Removes visibility of certain account of a process identified
638 * by a given UID to an application. This is called directly by the
639 * AccountManager and indirectly by the Authenticator.
640 *
641 * @param a Account to remove visibility from
642 * @param uid UID to remove visibility of the Account from
643 * @param ua UserAccount that hosts the account and application
644 *
645 * @return True if application access to account removed and was previously visible.
646 */
647 private boolean removeAccountVisibility(Account a, int uid, UserAccounts ua) {
648 int accountMapping = getMockAccountNumber(a, ua);
649 if(accountMapping < 0) {
650 return false;
651 }
652 synchronized(ua.cacheLock) {
653 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
654 ua.mVisibleListUidToMockAccountNumbers;
655 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
656 if(linkedAccountsToUid != null) {
657 boolean toReturn = linkedAccountsToUid.remove((Integer) accountMapping);
658 if(linkedAccountsToUid.size() == 0) {
659 userWlUidToMockAccountNums.remove(uid);
660 }
661 return toReturn;
662 }
663 }
664 return false;
665 }
666
667 /**
668 * Registers an application's preferences for supported account types for login. This is
669 * a helper method of requestAccountVisibility and indirectly called by AccountManager.
670 *
671 * @param accountTypes account types third party app is willing to support
672 * @param uid of application requesting account visibility
673 * @param ua UserAccount that hosts the account and application
674 */
675 private void addRequestedAccountsVisibility(String[] accountTypes, int uid, UserAccounts ua) {
676 synchronized(ua.cacheLock) {
677 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
678 ua.mApplicationAccountRequestMappings;
679 for(String accountType : accountTypes) {
680 ArrayList<Integer> accountUidAppList = userApplicationAccountRequestMappings
681 .get(accountType);
682 if(accountUidAppList == null) {
683 accountUidAppList = new ArrayList<>();
684 accountUidAppList.add(uid);
685 userApplicationAccountRequestMappings.put(accountType, accountUidAppList);
686 } else if (!accountUidAppList.contains(uid)) {
687 accountUidAppList.add(uid);
688 }
689 }
690 }
691 }
692
693 /**
694 * Registers the requested login account types requested by all the applications already
695 * installed on the device.
696 */
697 private void addRequestsForPreInstalledApplications() {
Fyodor Kupolovda993802016-09-21 14:47:10 -0700698 List<PackageInfo> allInstalledPackages = mPackageManager.getInstalledPackages(0);
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700699 for(PackageInfo pi : allInstalledPackages) {
700 int currentUid = pi.applicationInfo.uid;
701 if(currentUid != -1) {
702 registerAccountTypesSupported(currentUid,
703 getUserAccounts(UserHandle.getUserId(currentUid)));
704 }
705 }
706 }
707
708 /**
709 * Clears all preferences an application had for login account types it offered
710 * support for. This method is used by AccountManager after application is
711 * uninstalled.
712 *
713 * @param uid Uid of the application to clear account type preferences
714 * @param ua UserAccount that hosted the account and application
715 *
716 * @return true if any previous settings were overridden.
717 */
718 private boolean clearRequestedAccountVisibility(int uid, UserAccounts ua) {
719 boolean accountsDeleted = false;
720 ArrayList<String> accountTypesToRemove = new ArrayList<>();
721 synchronized(ua.cacheLock) {
722 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
723 ua.mApplicationAccountRequestMappings;
724 Set<Entry<String, ArrayList<Integer>>> accountTypeAppListEntries =
725 userApplicationAccountRequestMappings.entrySet();
726
727 for(Entry<String, ArrayList<Integer>> entry : accountTypeAppListEntries) {
728 ArrayList<Integer> supportedApps = entry.getValue();
729 if(supportedApps.remove((Integer) uid)) {
730 accountsDeleted = true;
731 }
732
733 if(supportedApps.isEmpty()) {
734 accountTypesToRemove.add(entry.getKey());
735 }
736 }
737
738 for(String s : accountTypesToRemove) {
739 userApplicationAccountRequestMappings.remove(s);
740 }
741 }
742
743 return accountsDeleted;
744 }
745
746 /**
747 * Retrieves the mock account number associated with an Account in order to later retrieve
748 * the account from the Integer-Account Mapping. An account number is not the same as
749 * accountId in the database. This method can be indirectly called by AccountManager and
750 * indirectly by the Authenticator.
751 *
752 * @param a account to retrieve account number mapping
753 * @param ua UserAccount that currently hosts the account and application
754 *
755 * @return account number affiliated with the Account in question. Negative number if none.
756 */
757 private int getMockAccountNumber(Account a, UserAccounts ua) {
758 //TODO: Each account is linked to AccountId rather than generated mock account numbers
759 SparseArray<Account> userAcctIdToAcctMap =
760 ua.mMockAccountIdToAccount;
761 synchronized(ua.cacheLock) {
762 int indexOfAccount = userAcctIdToAcctMap.indexOfValueByValue(a);
763 if(indexOfAccount < 0) {
764 return -1;
765 }
766 return userAcctIdToAcctMap.keyAt(indexOfAccount);
767 }
768 }
769
770 /**
771 * Returns a full list of accounts that a certain UID is allowed access
772 * based on the visible list entries.
773 *
774 * @param uid of application to retrieve visible listed accounts for
775 * @param ua UserAccount that currently hosts the account and application
776 *
777 * @return array of Account values that are accessible by the given uids
778 */
779 private Account[] getVisibleListedAccounts(int uid, UserAccounts ua) {
780 ArrayList<Account> visibleListedAccounts = new ArrayList<>();
781 synchronized(ua.cacheLock) {
782 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
783 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
784 ua.mVisibleListUidToMockAccountNumbers;
785 ArrayList<Integer> visibleListedUidAccountNumbers =
786 userWlUidToMockAccountNums.get(uid);
787 if(visibleListedUidAccountNumbers != null) {
788 for(Integer accountNumber : visibleListedUidAccountNumbers) {
789 Account currentAccount = userAcctIdToAcctMap.get(accountNumber);
790 visibleListedAccounts.add(currentAccount);
791 }
792 }
793 }
794 Account[] arrVisibleListedAccounts = new Account[visibleListedAccounts.size()];
795 return visibleListedAccounts.toArray(arrVisibleListedAccounts);
796 }
797
798 /**
799 * Makes an account number for a given Account to be mapped to.
800 * This method is called by makeVisible if an Account does not have
801 * a mapping for the visible list. This method is thus indirectly
802 * called by the Authenticator.
803 *
804 * @param a account to make an account number mapping of
805 * @param ua UserAccount that currently hosts the account and application
806 *
807 * @return account number created to map to the given account
808 */
809 // TODO: Remove this method and use accountId from DB.
810 private int makeAccountNumber(Account a, UserAccounts ua) {
811 synchronized(ua.cacheLock) {
812 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
813 int newAccountMapping = 0;
814 while(userAcctIdToAcctMap.get(newAccountMapping) != null) {
815 newAccountMapping++;
816 }
817 userAcctIdToAcctMap.put(newAccountMapping, a);
818 return newAccountMapping;
819 }
820 }
821
822
823
824 /**
825 * Registers an application, represented by a UID, to support account types detailed in
826 * the applications manifest as well as allowing it to opt for notifications.
827 *
828 * @param uid UID of application
829 * @param ua UserAccount that currently hosts the account and application
830 */
831 private void registerAccountTypesSupported(int uid, UserAccounts ua) {
832 /* Account types supported are drawn from the Android Manifest of the Application */
833 String interestedPackages = null;
834 try {
835 String[] allPackages = mPackageManager.getPackagesForUid(uid);
Nicolas Prevotf7d8df12016-09-16 17:45:34 +0100836 if (allPackages != null) {
837 for(String aPackage : allPackages) {
838 ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
839 PackageManager.GET_META_DATA);
840 Bundle b = ai.metaData;
841 if(b == null) {
842 return;
843 }
844 interestedPackages = b.getString("android.accounts.SupportedLoginTypes");
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700845 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700846 }
847 } catch (PackageManager.NameNotFoundException e) {
848 Log.d("NameNotFoundException", e.getMessage());
849 }
850 if(interestedPackages != null) {
851 /* request remote account types directly from here. Reads from Android Manifest */
852 requestAccountVisibility(interestedPackages.split(";"), uid, ua);
853 }
854 }
855
856 /**
857 * Allows AccountManager to register account types that an application has login
858 * support for. This method over-writes all of the application's previous settings
859 * for accounts it supported.
860 *
861 * @param accountTypes array of account types application wishes to support
862 * @param uid of application registering requested account types
863 * @param ua UserAccount that hosts the account and application
864 */
865 private void requestAccountVisibility(String[] accountTypes, int uid, UserAccounts ua) {
866 if(accountTypes.length > 0) {
867 clearRequestedAccountVisibility(uid, ua);
868 addRequestedAccountsVisibility(accountTypes, uid, ua);
869 }
870 }
871
872 /**
873 * Removes visibility of all Accounts to this particular UID. This is called when an
874 * application is uninstalled so another application that is installed with the same
875 * UID cannot access Accounts. This is called by AccountManager.
876 *
877 * @param uid of application to remove all Account visibility to
878 * @param ua UserAccount that hosts the current Account
879 */
880 private void removeAccountVisibilityAllAccounts(int uid, UserAccounts ua) {
881 synchronized(ua.cacheLock) {
882 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
883 ua.mVisibleListUidToMockAccountNumbers;
884 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
885 ArrayList<Integer> allAccountNumbersList = userWlUidToMockAccountNums.get(uid);
886 if(allAccountNumbersList != null) {
887 Integer[] allAccountNumbers = allAccountNumbersList.toArray(
888 new Integer[allAccountNumbersList.size()]);
889 for(int accountNum : allAccountNumbers) {
890 removeAccountVisibility(userAcctIdToAcctMap.get(accountNum), uid, ua);
891 }
892 }
893 }
894 }
895
896 /**
897 * Removes visible list functionality of a certain Account.
898 * This method is currently called by (1) addAccountExplicitly (as opposed to
899 * addAccountExplicitlyWithUid) and (2) removeAccountExplicitly.
900 *
901 * @param a the account to clear the visible list functionality for
902 * @param ua currently UserAccounts profile containing Account
903 *
904 * @return true if account previously had visible list functionality
905 */
906 private boolean removeVisibleListFunctionality(Account a, UserAccounts ua) {
907 int mockAccountNum = getMockAccountNumber(a, ua);
908 if(mockAccountNum < 0) {
909 return false;
910 }
911 synchronized(ua.cacheLock) {
912 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
913 ua.mVisibleListUidToMockAccountNumbers;
914 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
915
916 /* Removing mapping from account number to account removes visible list functionality*/
917 userAcctIdToAcctMap.remove(mockAccountNum);
918
919 for(int i = userWlUidToMockAccountNums.size() - 1 ; i >= 0 ; i--) {
920 int uidKey = userWlUidToMockAccountNums.keyAt(i);
921 ArrayList<Integer> allAccountNumbers = userWlUidToMockAccountNums.get(uidKey);
922 if(allAccountNumbers != null) {
923 allAccountNumbers.remove(mockAccountNum);
924 if(allAccountNumbers.isEmpty()) {
925 userWlUidToMockAccountNums.remove(uidKey);
926 }
927 }
928 }
929 }
930 return true;
931 }
932
933 /**
934 * Sends a direct intent to a package, notifying it of a visible account. This
935 * method is a helper method of makeAccountVisible.
936 *
937 * @param desiredPackage to send Account to
938 * @param visibleAccount to send to package
939 */
940 private void sendNotification(String desiredPackage, Account visibleAccount) {
941 Intent intent = new Intent();
942 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
943 intent.setAction(NEW_ACCOUNT_VISIBLE);
944 intent.setPackage(desiredPackage);
945 intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
946 mContext.sendBroadcast(intent);
947 }
948
949 @Override
Dianne Hackborn164371f2013-10-01 19:10:13 -0700950 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
951 throws RemoteException {
952 try {
953 return super.onTransact(code, data, reply, flags);
954 } catch (RuntimeException e) {
955 // The account manager only throws security exceptions, so let's
956 // log all others.
957 if (!(e instanceof SecurityException)) {
958 Slog.wtf(TAG, "Account Manager Crash", e);
959 }
960 throw e;
961 }
962 }
963
Amith Yamasani258848d2012-08-10 17:06:33 -0700964 private UserManager getUserManager() {
965 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700966 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -0700967 }
968 return mUserManager;
969 }
970
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700971 /**
972 * Validate internal set of accounts against installed authenticators for
973 * given user. Clears cached authenticators before validating.
974 */
975 public void validateAccounts(int userId) {
976 final UserAccounts accounts = getUserAccounts(userId);
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700977 // Invalidate user-specific cache to make sure we catch any
978 // removed authenticators.
979 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
980 }
981
982 /**
983 * Validate internal set of accounts against installed authenticators for
984 * given user. Clear cached authenticators before validating when requested.
985 */
986 private void validateAccountsInternal(
987 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700988 if (Log.isLoggable(TAG, Log.DEBUG)) {
989 Log.d(TAG, "validateAccountsInternal " + accounts.userId
Fyodor Kupolov00de49e2016-09-23 13:10:27 -0700990 + " isCeDatabaseAttached=" + accounts.accountsDb.isCeDatabaseAttached()
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600991 + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700992 }
Carlos Valdiviaa46b1122016-04-26 19:36:50 -0700993
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700994 if (invalidateAuthenticatorCache) {
995 mAuthenticatorCache.invalidateCache(accounts.userId);
996 }
997
Carlos Valdiviaa46b1122016-04-26 19:36:50 -0700998 final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
999 mAuthenticatorCache, accounts.userId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001000 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001001
Amith Yamasani04e0d262012-02-14 11:50:53 -08001002 synchronized (accounts.cacheLock) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001003 boolean accountDeleted = false;
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001004
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001005 // Get a map of stored authenticator types to UID
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001006 final AccountsDb accountsDb = accounts.accountsDb;
1007 Map<String, Integer> metaAuthUid = accountsDb.findMetaAuthUid();
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001008 // Create a list of authenticator type whose previous uid no longer exists
1009 HashSet<String> obsoleteAuthType = Sets.newHashSet();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001010 SparseBooleanArray knownUids = null;
1011 for (Entry<String, Integer> authToUidEntry : metaAuthUid.entrySet()) {
1012 String type = authToUidEntry.getKey();
1013 int uid = authToUidEntry.getValue();
1014 Integer knownUid = knownAuth.get(type);
1015 if (knownUid != null && uid == knownUid) {
1016 // Remove it from the knownAuth list if it's unchanged.
1017 knownAuth.remove(type);
1018 } else {
1019 /*
1020 * The authenticator is presently not cached and should only be triggered
1021 * when we think an authenticator has been removed (or is being updated).
1022 * But we still want to check if any data with the associated uid is
1023 * around. This is an (imperfect) signal that the package may be updating.
1024 *
1025 * A side effect of this is that an authenticator sharing a uid with
1026 * multiple apps won't get its credentials wiped as long as some app with
1027 * that uid is still on the device. But I suspect that this is a rare case.
1028 * And it isn't clear to me how an attacker could really exploit that
1029 * feature.
1030 *
1031 * The upshot is that we don't have to worry about accounts getting
1032 * uninstalled while the authenticator's package is being updated.
1033 *
1034 */
1035 if (knownUids == null) {
1036 knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001037 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001038 if (!knownUids.get(uid)) {
1039 // The authenticator is not presently available to the cache. And the
1040 // package no longer has a data directory (so we surmise it isn't updating).
1041 // So purge its data from the account databases.
1042 obsoleteAuthType.add(type);
1043 // And delete it from the TABLE_META
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001044 accountsDb.deleteMetaByAuthTypeAndUid(type, uid);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001045 }
1046 }
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001047 }
1048
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001049 // Add the newly registered authenticator to TABLE_META. If old authenticators have
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001050 // been re-enabled (after being updated for example), then we just overwrite the old
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001051 // values.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001052 for (Entry<String, Integer> entry : knownAuth.entrySet()) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001053 accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001054 }
1055
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001056 final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001057 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001058 accounts.accountCache.clear();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001059 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001060 for (Entry<Long, Account> accountEntry : accountsMap.entrySet()) {
1061 final long accountId = accountEntry.getKey();
1062 final Account account = accountEntry.getValue();
1063 if (obsoleteAuthType.contains(account.type)) {
1064 Slog.w(TAG, "deleting account " + account.name + " because type "
1065 + account.type + "'s registered authenticator no longer exist.");
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001066 accountsDb.beginTransaction();
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001067 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001068 accountsDb.deleteDeAccount(accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001069 // Also delete from CE table if user is unlocked; if user is currently
1070 // locked the account will be removed later by syncDeCeAccountsLocked
1071 if (userUnlocked) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001072 accountsDb.deleteCeAccount(accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001073 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001074 accountsDb.setTransactionSuccessful();
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001075 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001076 accountsDb.endTransaction();
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001077 }
Fred Quintana56285a62010-12-02 14:20:51 -08001078 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001079
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001080 logRecord(AccountsDb.DEBUG_ACTION_AUTHENTICATOR_REMOVE,
1081 AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001082
Amith Yamasani04e0d262012-02-14 11:50:53 -08001083 accounts.userDataCache.remove(account);
1084 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001085 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08001086 } else {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001087 ArrayList<String> accountNames = accountNamesByType.get(account.type);
Fred Quintana56285a62010-12-02 14:20:51 -08001088 if (accountNames == null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001089 accountNames = new ArrayList<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001090 accountNamesByType.put(account.type, accountNames);
Fred Quintana56285a62010-12-02 14:20:51 -08001091 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001092 accountNames.add(account.name);
Fred Quintana56285a62010-12-02 14:20:51 -08001093 }
1094 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001095 for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -08001096 final String accountType = cur.getKey();
1097 final ArrayList<String> accountNames = cur.getValue();
1098 final Account[] accountsForType = new Account[accountNames.size()];
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001099 for (int i = 0; i < accountsForType.length; i++) {
Svet Ganovf6d424f12016-09-20 20:18:53 -07001100 accountsForType[i] = new Account(accountNames.get(i), accountType,
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07001101 UUID.randomUUID().toString());
Fred Quintana56285a62010-12-02 14:20:51 -08001102 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001103 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -08001104 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001105 } finally {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001106 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001107 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001108 }
Fred Quintanaafa92b82009-12-01 16:27:03 -08001109 }
1110 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001111 }
1112
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001113 private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
1114 // Get the UIDs of all apps that might have data on the device. We want
1115 // to preserve user data if the app might otherwise be storing data.
1116 List<PackageInfo> pkgsWithData =
1117 mPackageManager.getInstalledPackagesAsUser(
1118 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
1119 SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
1120 for (PackageInfo pkgInfo : pkgsWithData) {
1121 if (pkgInfo.applicationInfo != null
1122 && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
1123 knownUids.put(pkgInfo.applicationInfo.uid, true);
1124 }
1125 }
1126 return knownUids;
1127 }
1128
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001129 static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001130 Context context,
1131 int userId) {
1132 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001133 return getAuthenticatorTypeAndUIDForUser(authCache, userId);
1134 }
1135
1136 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1137 IAccountAuthenticatorCache authCache,
1138 int userId) {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001139 HashMap<String, Integer> knownAuth = new HashMap<>();
1140 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
1141 .getAllServices(userId)) {
1142 knownAuth.put(service.type.type, service.uid);
1143 }
1144 return knownAuth;
1145 }
1146
Amith Yamasani04e0d262012-02-14 11:50:53 -08001147 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001148 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001149 }
1150
1151 protected UserAccounts getUserAccounts(int userId) {
1152 synchronized (mUsers) {
1153 UserAccounts accounts = mUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001154 boolean validateAccounts = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001155 if (accounts == null) {
Fyodor Kupolovda993802016-09-21 14:47:10 -07001156 File preNDbFile = new File(mInjector.getPreNDatabaseName(userId));
1157 File deDbFile = new File(mInjector.getDeDatabaseName(userId));
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001158 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001159 initializeDebugDbSizeAndCompileSqlStatementForLogging(accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001160 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001161 purgeOldGrants(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001162 validateAccounts = true;
1163 }
1164 // open CE database if necessary
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001165 if (!accounts.accountsDb.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001166 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
1167 synchronized (accounts.cacheLock) {
Fyodor Kupolovda993802016-09-21 14:47:10 -07001168 File ceDatabaseFile = new File(mInjector.getCeDatabaseName(userId));
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001169 accounts.accountsDb.attachCeDatabase(ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001170 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001171 syncDeCeAccountsLocked(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001172 }
1173 if (validateAccounts) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001174 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001175 }
1176 return accounts;
1177 }
1178 }
1179
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001180 private void syncDeCeAccountsLocked(UserAccounts accounts) {
1181 Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001182 List<Account> accountsToRemove = accounts.accountsDb.findCeAccountsNotInDe();
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001183 if (!accountsToRemove.isEmpty()) {
1184 Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
1185 + accounts.userId + " was locked. Removing accounts from CE tables");
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001186 logRecord(accounts, AccountsDb.DEBUG_ACTION_SYNC_DE_CE_ACCOUNTS,
1187 AccountsDb.TABLE_ACCOUNTS);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001188
1189 for (Account account : accountsToRemove) {
1190 removeAccountInternal(accounts, account, Process.myUid());
1191 }
1192 }
1193 }
1194
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001195 private void purgeOldGrantsAll() {
1196 synchronized (mUsers) {
1197 for (int i = 0; i < mUsers.size(); i++) {
1198 purgeOldGrants(mUsers.valueAt(i));
1199 }
1200 }
1201 }
1202
1203 private void purgeOldGrants(UserAccounts accounts) {
1204 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001205 List<Integer> uids = accounts.accountsDb.findAllUidGrants();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001206 for (int uid : uids) {
1207 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
1208 if (packageExists) {
1209 continue;
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001210 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001211 Log.d(TAG, "deleting grants for UID " + uid
1212 + " because its package is no longer installed");
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001213 accounts.accountsDb.deleteGrantsByUid(uid);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001214 }
1215 }
1216 }
1217
Amith Yamasani13593602012-03-22 16:16:17 -07001218 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -07001219 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -07001220 if (userId < 1) return;
1221
1222 UserAccounts accounts;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001223 boolean userUnlocked;
Amith Yamasani13593602012-03-22 16:16:17 -07001224 synchronized (mUsers) {
1225 accounts = mUsers.get(userId);
1226 mUsers.remove(userId);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001227 userUnlocked = mLocalUnlockedUsers.get(userId);
1228 mLocalUnlockedUsers.delete(userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001229 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001230 if (accounts != null) {
1231 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001232 accounts.accountsDb.close();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001233 }
Amith Yamasani13593602012-03-22 16:16:17 -07001234 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001235 Log.i(TAG, "Removing database files for user " + userId);
Fyodor Kupolovda993802016-09-21 14:47:10 -07001236 File dbFile = new File(mInjector.getDeDatabaseName(userId));
Amith Yamasani13593602012-03-22 16:16:17 -07001237
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001238 AccountsDb.deleteDbFileWarnIfFailed(dbFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001239 // Remove CE file if user is unlocked, or FBE is not enabled
1240 boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
1241 if (!fbeEnabled || userUnlocked) {
Fyodor Kupolovda993802016-09-21 14:47:10 -07001242 File ceDb = new File(mInjector.getCeDatabaseName(userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001243 if (ceDb.exists()) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001244 AccountsDb.deleteDbFileWarnIfFailed(ceDb);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001245 }
1246 }
1247 }
1248
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001249 @VisibleForTesting
1250 void onUserUnlocked(Intent intent) {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -06001251 onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
1252 }
1253
1254 void onUnlockUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001255 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1256 Log.v(TAG, "onUserUnlocked " + userId);
1257 }
1258 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001259 mLocalUnlockedUsers.put(userId, true);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001260 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001261 if (userId < 1) return;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001262 syncSharedAccounts(userId);
1263 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001264
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001265 private void syncSharedAccounts(int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001266 // Check if there's a shared account that needs to be created as an account
1267 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
1268 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -07001269 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001270 int parentUserId = UserManager.isSplitSystemUser()
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001271 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001272 : UserHandle.USER_SYSTEM;
1273 if (parentUserId < 0) {
1274 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
1275 return;
1276 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001277 for (Account sa : sharedAccounts) {
1278 if (ArrayUtils.contains(accounts, sa)) continue;
1279 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001280 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001281 }
1282 }
1283
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001284 @Override
1285 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001286 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -07001287 }
1288
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001289 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001290 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001291 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001292 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1293 Log.v(TAG, "getPassword: " + account
1294 + ", caller's uid " + Binder.getCallingUid()
1295 + ", pid " + Binder.getCallingPid());
1296 }
Fred Quintana382601f2010-03-25 12:25:10 -07001297 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001298 int userId = UserHandle.getCallingUserId();
1299 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001300 String msg = String.format(
1301 "uid %s cannot get secrets for accounts of type: %s",
1302 callingUid,
1303 account.type);
1304 throw new SecurityException(msg);
1305 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001306 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001307 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001308 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001309 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001310 } finally {
1311 restoreCallingIdentity(identityToken);
1312 }
1313 }
1314
Amith Yamasani04e0d262012-02-14 11:50:53 -08001315 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -07001316 if (account == null) {
1317 return null;
1318 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001319 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001320 Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
1321 return null;
1322 }
Fred Quintana31957f12009-10-21 13:43:10 -07001323
Amith Yamasani04e0d262012-02-14 11:50:53 -08001324 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001325 return accounts.accountsDb.findAccountPasswordByNameAndType(account.name, account.type);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001326 }
1327 }
1328
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001329 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001330 public String getPreviousName(Account account) {
1331 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1332 Log.v(TAG, "getPreviousName: " + account
1333 + ", caller's uid " + Binder.getCallingUid()
1334 + ", pid " + Binder.getCallingPid());
1335 }
1336 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001337 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001338 long identityToken = clearCallingIdentity();
1339 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001340 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001341 return readPreviousNameInternal(accounts, account);
1342 } finally {
1343 restoreCallingIdentity(identityToken);
1344 }
1345 }
1346
1347 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
1348 if (account == null) {
1349 return null;
1350 }
1351 synchronized (accounts.cacheLock) {
1352 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
1353 if (previousNameRef == null) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001354 String previousName = accounts.accountsDb.findDeAccountPreviousName(account);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001355 previousNameRef = new AtomicReference<>(previousName);
1356 accounts.previousNameCache.put(account, previousNameRef);
1357 return previousName;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001358 } else {
1359 return previousNameRef.get();
1360 }
1361 }
1362 }
1363
1364 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001365 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001366 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001367 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001368 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
1369 account, key, callingUid, Binder.getCallingPid());
1370 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -08001371 }
Fred Quintana382601f2010-03-25 12:25:10 -07001372 if (account == null) throw new IllegalArgumentException("account is null");
1373 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001374 int userId = UserHandle.getCallingUserId();
1375 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001376 String msg = String.format(
1377 "uid %s cannot get user data for accounts of type: %s",
1378 callingUid,
1379 account.type);
1380 throw new SecurityException(msg);
1381 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001382 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07001383 Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
1384 return null;
1385 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001386 long identityToken = clearCallingIdentity();
1387 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001388 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00001389 synchronized (accounts.cacheLock) {
1390 if (!accountExistsCacheLocked(accounts, account)) {
1391 return null;
1392 }
1393 return readUserDataInternalLocked(accounts, account, key);
1394 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001395 } finally {
1396 restoreCallingIdentity(identityToken);
1397 }
1398 }
1399
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001400 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001401 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001402 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001403 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1404 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001405 + "for user id " + userId
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001406 + " caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001407 + ", pid " + Binder.getCallingPid());
1408 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001409 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001410 if (isCrossUser(callingUid, userId)) {
1411 throw new SecurityException(
1412 String.format(
1413 "User %s tying to get authenticator types for %s" ,
1414 UserHandle.getCallingUserId(),
1415 userId));
1416 }
1417
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001418 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001419 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001420 return getAuthenticatorTypesInternal(userId);
1421
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001422 } finally {
1423 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001424 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001425 }
1426
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001427 /**
1428 * Should only be called inside of a clearCallingIdentity block.
1429 */
1430 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
Fyodor Kupolov81446482016-08-24 11:27:49 -07001431 mAuthenticatorCache.updateServices(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001432 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
1433 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
1434 AuthenticatorDescription[] types =
1435 new AuthenticatorDescription[authenticatorCollection.size()];
1436 int i = 0;
1437 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
1438 : authenticatorCollection) {
1439 types[i] = authenticator.type;
1440 i++;
1441 }
1442 return types;
1443 }
1444
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001445 private boolean isCrossUser(int callingUid, int userId) {
1446 return (userId != UserHandle.getCallingUserId()
1447 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001448 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001449 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1450 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001451 }
1452
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001453 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07001454 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001455 Bundle.setDefusable(extras, true);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001456 // clears the visible list functionality for this account because this method allows
1457 // default account access to all applications for account.
1458
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001459 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001460 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001461 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001462 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001463 + ", pid " + Binder.getCallingPid());
1464 }
Fred Quintana382601f2010-03-25 12:25:10 -07001465 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001466 int userId = UserHandle.getCallingUserId();
1467 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001468 String msg = String.format(
1469 "uid %s cannot explicitly add accounts of type: %s",
1470 callingUid,
1471 account.type);
1472 throw new SecurityException(msg);
1473 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001474 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001475 /*
1476 * Child users are not allowed to add accounts. Only the accounts that are
1477 * shared by the parent profile can be added to child profile.
1478 *
1479 * TODO: Only allow accounts that were shared to be added by
1480 * a limited user.
1481 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001482
Fred Quintana60307342009-03-24 22:48:12 -07001483 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001484 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001485 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001486 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001487 return addAccountInternal(accounts, account, password, extras, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -07001488 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001489 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001490 }
1491 }
1492
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001493 @Override
1494 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001495 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001496 int callingUid = Binder.getCallingUid();
1497 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1498 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001499 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001500 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001501 final UserAccounts fromAccounts = getUserAccounts(userFrom);
1502 final UserAccounts toAccounts = getUserAccounts(userTo);
1503 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001504 if (response != null) {
1505 Bundle result = new Bundle();
1506 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1507 try {
1508 response.onResult(result);
1509 } catch (RemoteException e) {
1510 Slog.w(TAG, "Failed to report error back to the client." + e);
1511 }
1512 }
1513 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -08001514 }
1515
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001516 Slog.d(TAG, "Copying account " + account.name
1517 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001518 long identityToken = clearCallingIdentity();
1519 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001520 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001521 false /* stripAuthTokenFromResult */, account.name,
1522 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001523 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001524 protected String toDebugString(long now) {
1525 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1526 + ", " + account.type;
1527 }
1528
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001529 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001530 public void run() throws RemoteException {
1531 mAuthenticator.getAccountCredentialsForCloning(this, account);
1532 }
1533
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001534 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001535 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001536 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001537 if (result != null
1538 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1539 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001540 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001541 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001542 super.onResult(result);
1543 }
1544 }
1545 }.bind();
1546 } finally {
1547 restoreCallingIdentity(identityToken);
1548 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001549 }
1550
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001551 @Override
1552 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001553 final int callingUid = Binder.getCallingUid();
1554 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1555 String msg = String.format(
1556 "accountAuthenticated( account: %s, callerUid: %s)",
1557 account,
1558 callingUid);
1559 Log.v(TAG, msg);
1560 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001561 if (account == null) {
1562 throw new IllegalArgumentException("account is null");
1563 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001564 int userId = UserHandle.getCallingUserId();
1565 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001566 String msg = String.format(
1567 "uid %s cannot notify authentication for accounts of type: %s",
1568 callingUid,
1569 account.type);
1570 throw new SecurityException(msg);
1571 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001572
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001573 if (!canUserModifyAccounts(userId, callingUid) ||
1574 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001575 return false;
1576 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001577
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001578 long identityToken = clearCallingIdentity();
1579 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001580 UserAccounts accounts = getUserAccounts(userId);
1581 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001582 } finally {
1583 restoreCallingIdentity(identityToken);
1584 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07001585 }
1586
1587 private boolean updateLastAuthenticatedTime(Account account) {
1588 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001589 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001590 return accounts.accountsDb.updateAccountLastAuthenticatedTime(account);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001591 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001592 }
1593
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001594 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001595 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1596 final int parentUserId){
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001597 Bundle.setDefusable(accountCredentials, true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001598 long id = clearCallingIdentity();
1599 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001600 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001601 false /* stripAuthTokenFromResult */, account.name,
1602 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001603 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001604 protected String toDebugString(long now) {
1605 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1606 + ", " + account.type;
1607 }
1608
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001609 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001610 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001611 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001612 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001613 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -07001614 for (Account acc : getAccounts(parentUserId,
1615 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001616 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001617 mAuthenticator.addAccountFromCredentials(
1618 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001619 break;
1620 }
1621 }
1622 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001623 }
1624
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001625 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001626 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001627 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001628 // TODO: Anything to do if if succedded?
1629 // TODO: If it failed: Show error notification? Should we remove the shadow
1630 // account to avoid retries?
1631 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001632 }
1633
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001634 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001635 public void onError(int errorCode, String errorMessage) {
1636 super.onError(errorCode, errorMessage);
1637 // TODO: Show error notification to user
1638 // TODO: Should we remove the shadow account so that it doesn't keep trying?
1639 }
1640
1641 }.bind();
1642 } finally {
1643 restoreCallingIdentity(id);
1644 }
1645 }
1646
Amith Yamasani04e0d262012-02-14 11:50:53 -08001647 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001648 Bundle extras, int callingUid) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001649 Bundle.setDefusable(extras, true);
Fred Quintana743dfad2010-07-15 10:59:25 -07001650 if (account == null) {
1651 return false;
1652 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001653 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001654 Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
1655 + " is locked. callingUid=" + callingUid);
1656 return false;
1657 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001658 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001659 accounts.accountsDb.beginTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001660 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001661 if (accounts.accountsDb.findCeAccountId(account) >= 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001662 Log.w(TAG, "insertAccountIntoDatabase: " + account
1663 + ", skipping since the account already exists");
1664 return false;
1665 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001666 long accountId = accounts.accountsDb.insertCeAccount(account, password);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001667 if (accountId < 0) {
1668 Log.w(TAG, "insertAccountIntoDatabase: " + account
1669 + ", skipping the DB insert failed");
1670 return false;
1671 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001672 // Insert into DE table
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001673 if (accounts.accountsDb.insertDeAccount(account, accountId) < 0) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001674 Log.w(TAG, "insertAccountIntoDatabase: " + account
1675 + ", skipping the DB insert failed");
1676 return false;
1677 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001678 if (extras != null) {
1679 for (String key : extras.keySet()) {
1680 final String value = extras.getString(key);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001681 if (accounts.accountsDb.insertExtra(accountId, key, value) < 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001682 Log.w(TAG, "insertAccountIntoDatabase: " + account
1683 + ", skipping since insertExtra failed for key " + key);
1684 return false;
1685 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001686 }
1687 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001688 accounts.accountsDb.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001689
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001690 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
1691 accountId, accounts, callingUid);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001692
Amith Yamasani04e0d262012-02-14 11:50:53 -08001693 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001694 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001695 accounts.accountsDb.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001696 }
Amith Yamasani5be347b2013-03-31 17:44:31 -07001697 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001698 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1699 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001700 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001701
1702 // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1703 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001704 return true;
1705 }
1706
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001707 private boolean isLocalUnlockedUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001708 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001709 return mLocalUnlockedUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001710 }
1711 }
1712
Amith Yamasani5be347b2013-03-31 17:44:31 -07001713 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001714 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -07001715 * running, then clone the account too.
1716 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001717 *
Amith Yamasani5be347b2013-03-31 17:44:31 -07001718 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001719 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -07001720 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -07001721 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001722 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001723 addSharedAccountAsUser(account, user.id);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001724 if (isLocalUnlockedUser(user.id)) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07001725 mHandler.sendMessage(mHandler.obtainMessage(
Fyodor Kupolov041232a2016-02-22 15:01:45 -08001726 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -07001727 }
1728 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001729 }
1730 }
1731
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001732 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001733 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001734 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001735 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001736 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1737 Log.v(TAG, "hasFeatures: " + account
1738 + ", response " + response
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001739 + ", features " + Arrays.toString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001740 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001741 + ", pid " + Binder.getCallingPid());
1742 }
Fred Quintana382601f2010-03-25 12:25:10 -07001743 if (response == null) throw new IllegalArgumentException("response is null");
1744 if (account == null) throw new IllegalArgumentException("account is null");
1745 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001746 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001747 checkReadAccountsPermitted(callingUid, account.type, userId,
1748 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001749
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001750 long identityToken = clearCallingIdentity();
1751 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001752 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001753 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001754 } finally {
1755 restoreCallingIdentity(identityToken);
1756 }
1757 }
1758
1759 private class TestFeaturesSession extends Session {
1760 private final String[] mFeatures;
1761 private final Account mAccount;
1762
Amith Yamasani04e0d262012-02-14 11:50:53 -08001763 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001764 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001765 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001766 true /* stripAuthTokenFromResult */, account.name,
1767 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001768 mFeatures = features;
1769 mAccount = account;
1770 }
1771
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001772 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001773 public void run() throws RemoteException {
1774 try {
1775 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1776 } catch (RemoteException e) {
1777 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1778 }
1779 }
1780
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001781 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001782 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001783 Bundle.setDefusable(result, true);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001784 IAccountManagerResponse response = getResponseAndClose();
1785 if (response != null) {
1786 try {
1787 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001788 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001789 return;
1790 }
Fred Quintana56285a62010-12-02 14:20:51 -08001791 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1792 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1793 + response);
1794 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001795 final Bundle newResult = new Bundle();
1796 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1797 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1798 response.onResult(newResult);
1799 } catch (RemoteException e) {
1800 // if the caller is dead then there is no one to care about remote exceptions
1801 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1802 Log.v(TAG, "failure while notifying response", e);
1803 }
1804 }
1805 }
1806 }
1807
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001808 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001809 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001810 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001811 + ", " + mAccount
1812 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1813 }
1814 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001815
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001816 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001817 public void renameAccount(
1818 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001819 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001820 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1821 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001822 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001823 + ", pid " + Binder.getCallingPid());
1824 }
1825 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001826 int userId = UserHandle.getCallingUserId();
1827 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001828 String msg = String.format(
1829 "uid %s cannot rename accounts of type: %s",
1830 callingUid,
1831 accountToRename.type);
1832 throw new SecurityException(msg);
1833 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001834 long identityToken = clearCallingIdentity();
1835 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001836 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001837 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001838 Bundle result = new Bundle();
1839 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1840 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07001841 result.putString(AccountManager.KEY_ACCOUNT_ACCESS_ID,
1842 resultingAccount.getAccessId());
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001843 try {
1844 response.onResult(result);
1845 } catch (RemoteException e) {
1846 Log.w(TAG, e.getMessage());
1847 }
1848 } finally {
1849 restoreCallingIdentity(identityToken);
1850 }
1851 }
1852
1853 private Account renameAccountInternal(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001854 UserAccounts accounts, Account accountToRename, String newName) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001855 Account resultAccount = null;
1856 /*
1857 * Cancel existing notifications. Let authenticators
1858 * re-post notifications as required. But we don't know if
1859 * the authenticators have bound their notifications to
1860 * now stale account name data.
1861 *
1862 * With a rename api, we might not need to do this anymore but it
1863 * shouldn't hurt.
1864 */
1865 cancelNotification(
1866 getSigninRequiredNotificationId(accounts, accountToRename),
1867 new UserHandle(accounts.userId));
1868 synchronized(accounts.credentialsPermissionNotificationIds) {
1869 for (Pair<Pair<Account, String>, Integer> pair:
1870 accounts.credentialsPermissionNotificationIds.keySet()) {
1871 if (accountToRename.equals(pair.first.first)) {
1872 int id = accounts.credentialsPermissionNotificationIds.get(pair);
1873 cancelNotification(id, new UserHandle(accounts.userId));
1874 }
1875 }
1876 }
1877 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001878 accounts.accountsDb.beginTransaction();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001879 Account renamedAccount = new Account(newName, accountToRename.type);
1880 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001881 final long accountId = accounts.accountsDb.findDeAccountId(accountToRename);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001882 if (accountId >= 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001883 accounts.accountsDb.renameCeAccount(accountId, newName);
1884 accounts.accountsDb.renameDeAccount(accountId, newName, accountToRename.name);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001885 }
1886 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07001887 accounts.accountsDb.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001888 }
1889 /*
1890 * Database transaction was successful. Clean up cached
1891 * data associated with the account in the user profile.
1892 */
Svet Ganov5c4a5122016-09-23 19:29:11 -07001893 renamedAccount = insertAccountIntoCacheLocked(accounts, renamedAccount);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001894 /*
1895 * Extract the data and token caches before removing the
1896 * old account to preserve the user data associated with
1897 * the account.
1898 */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001899 Map<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1900 Map<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001901 removeAccountFromCacheLocked(accounts, accountToRename);
1902 /*
1903 * Update the cached data associated with the renamed
1904 * account.
1905 */
1906 accounts.userDataCache.put(renamedAccount, tmpData);
1907 accounts.authTokenCache.put(renamedAccount, tmpTokens);
1908 accounts.previousNameCache.put(
1909 renamedAccount,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001910 new AtomicReference<>(accountToRename.name));
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001911 resultAccount = renamedAccount;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001912
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001913 int parentUserId = accounts.userId;
1914 if (canHaveProfile(parentUserId)) {
1915 /*
1916 * Owner or system user account was renamed, rename the account for
1917 * those users with which the account was shared.
1918 */
1919 List<UserInfo> users = getUserManager().getUsers(true);
1920 for (UserInfo user : users) {
1921 if (user.isRestricted()
1922 && (user.restrictedProfileParentId == parentUserId)) {
1923 renameSharedAccountAsUser(accountToRename, newName, user.id);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001924 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001925 }
1926 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001927 sendAccountsChangedBroadcast(accounts.userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001928 }
1929 return resultAccount;
1930 }
1931
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001932 private boolean canHaveProfile(final int parentUserId) {
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001933 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001934 return userInfo != null && userInfo.canHaveProfile();
1935 }
1936
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001937 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001938 public void removeAccount(IAccountManagerResponse response, Account account,
1939 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001940 removeAccountAsUser(
1941 response,
1942 account,
1943 expectActivityLaunch,
1944 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001945 }
1946
1947 @Override
1948 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001949 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001950 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001951 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1952 Log.v(TAG, "removeAccount: " + account
1953 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001954 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001955 + ", pid " + Binder.getCallingPid()
1956 + ", for user id " + userId);
1957 }
1958 if (response == null) throw new IllegalArgumentException("response is null");
1959 if (account == null) throw new IllegalArgumentException("account is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001960 // Only allow the system process to modify accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001961 if (isCrossUser(callingUid, userId)) {
1962 throw new SecurityException(
1963 String.format(
1964 "User %s tying remove account for %s" ,
1965 UserHandle.getCallingUserId(),
1966 userId));
1967 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001968 /*
1969 * Only the system or authenticator should be allowed to remove accounts for that
1970 * authenticator. This will let users remove accounts (via Settings in the system) but not
1971 * arbitrary applications (like competing authenticators).
1972 */
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001973 UserHandle user = UserHandle.of(userId);
Ian Pedowitz358e51f2016-03-15 17:08:27 +00001974 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
1975 && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001976 String msg = String.format(
1977 "uid %s cannot remove accounts of type: %s",
1978 callingUid,
1979 account.type);
1980 throw new SecurityException(msg);
1981 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001982 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001983 try {
1984 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1985 "User cannot modify accounts");
1986 } catch (RemoteException re) {
1987 }
1988 return;
1989 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001990 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001991 try {
1992 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1993 "User cannot modify accounts of this type (policy).");
1994 } catch (RemoteException re) {
1995 }
1996 return;
1997 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001998 long identityToken = clearCallingIdentity();
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001999 UserAccounts accounts = getUserAccounts(userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002000 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002001 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002002 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08002003 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002004 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002005 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002006 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002007 }
2008 }
2009 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002010 final long accountId = accounts.accountsDb.findDeAccountId(account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002011 logRecord(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002012 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
2013 AccountsDb.TABLE_ACCOUNTS,
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002014 accountId,
2015 accounts,
2016 callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002017 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002018 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
2019 } finally {
2020 restoreCallingIdentity(identityToken);
2021 }
2022 }
2023
2024 @Override
2025 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002026 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002027 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2028 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002029 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002030 + ", pid " + Binder.getCallingPid());
2031 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002032 int userId = Binder.getCallingUserHandle().getIdentifier();
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002033 if (account == null) {
2034 /*
2035 * Null accounts should result in returning false, as per
2036 * AccountManage.addAccountExplicitly(...) java doc.
2037 */
2038 Log.e(TAG, "account is null");
2039 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002040 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002041 String msg = String.format(
2042 "uid %s cannot explicitly add accounts of type: %s",
2043 callingUid,
2044 account.type);
2045 throw new SecurityException(msg);
2046 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07002047 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002048 UserAccounts accounts = getUserAccountsForCaller();
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002049 final long accountId = accounts.accountsDb.findDeAccountId(account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002050 logRecord(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002051 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
2052 AccountsDb.TABLE_ACCOUNTS,
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002053 accountId,
2054 accounts,
2055 callingUid);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002056 long identityToken = clearCallingIdentity();
2057 try {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002058 return removeAccountInternal(accounts, account, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002059 } finally {
2060 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07002061 }
Fred Quintana60307342009-03-24 22:48:12 -07002062 }
2063
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002064 private class RemoveAccountSession extends Session {
2065 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002066 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002067 Account account, boolean expectActivityLaunch) {
2068 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002069 true /* stripAuthTokenFromResult */, account.name,
2070 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002071 mAccount = account;
2072 }
2073
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002074 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002075 protected String toDebugString(long now) {
2076 return super.toDebugString(now) + ", removeAccount"
2077 + ", account " + mAccount;
2078 }
2079
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002080 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002081 public void run() throws RemoteException {
2082 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
2083 }
2084
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002085 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002086 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002087 Bundle.setDefusable(result, true);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002088 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
2089 && !result.containsKey(AccountManager.KEY_INTENT)) {
2090 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002091 if (removalAllowed) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002092 removeAccountInternal(mAccounts, mAccount, getCallingUid());
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002093 }
2094 IAccountManagerResponse response = getResponseAndClose();
2095 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08002096 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2097 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2098 + response);
2099 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002100 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002101 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002102 try {
2103 response.onResult(result2);
2104 } catch (RemoteException e) {
2105 // ignore
2106 }
2107 }
2108 }
2109 super.onResult(result);
2110 }
2111 }
2112
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07002113 @VisibleForTesting
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002114 protected void removeAccountInternal(Account account) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002115 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
Amith Yamasani04e0d262012-02-14 11:50:53 -08002116 }
2117
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002118 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002119 boolean isChanged = false;
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002120 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002121 if (!userUnlocked) {
2122 Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
2123 + " is still locked. CE data will be removed later");
2124 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002125 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002126 accounts.accountsDb.beginTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002127 // Set to a dummy value, this will only be used if the database
2128 // transaction succeeds.
2129 long accountId = -1;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002130 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002131 accountId = accounts.accountsDb.findDeAccountId(account);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002132 if (accountId >= 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002133 accounts.accountsDb.deleteDeAccount(accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002134 if (userUnlocked) {
2135 // Delete from CE table
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002136 accounts.accountsDb.deleteCeAccount(accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002137 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002138 accounts.accountsDb.setTransactionSuccessful();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002139 isChanged = true;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002140 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002141 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002142 accounts.accountsDb.endTransaction();
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002143 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002144 if (isChanged) {
2145 removeAccountFromCacheLocked(accounts, account);
2146 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
2147 sendAccountsChangedBroadcast(accounts.userId);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002148 String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
2149 : AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
2150 logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002151 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002152 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002153 long id = Binder.clearCallingIdentity();
2154 try {
2155 int parentUserId = accounts.userId;
2156 if (canHaveProfile(parentUserId)) {
2157 // Remove from any restricted profiles that are sharing this account.
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002158 List<UserInfo> users = getUserManager().getUsers(true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002159 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002160 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002161 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002162 }
2163 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08002164 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002165 } finally {
2166 Binder.restoreCallingIdentity(id);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002167 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002168
2169 if (isChanged) {
2170 synchronized (accounts.credentialsPermissionNotificationIds) {
2171 for (Pair<Pair<Account, String>, Integer> key
2172 : accounts.credentialsPermissionNotificationIds.keySet()) {
2173 if (account.equals(key.first.first)
Svet Ganovf6d424f12016-09-20 20:18:53 -07002174 && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002175 final int uid = (Integer) key.second;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07002176 mHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002177 account, uid, false));
2178 }
2179 }
2180 }
2181 }
2182
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002183 return isChanged;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002184 }
2185
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002186 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002187 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002188 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002189 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2190 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07002191 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08002192 + ", pid " + Binder.getCallingPid());
2193 }
Fred Quintana382601f2010-03-25 12:25:10 -07002194 if (accountType == null) throw new IllegalArgumentException("accountType is null");
2195 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002196 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002197 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002198 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002199 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002200 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002201 accounts.accountsDb.beginTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002202 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002203 invalidateAuthTokenLocked(accounts, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002204 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002205 accounts.accountsDb.setTransactionSuccessful();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002206 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002207 accounts.accountsDb.endTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002208 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002209 }
Fred Quintana60307342009-03-24 22:48:12 -07002210 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002211 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002212 }
2213 }
2214
Carlos Valdivia91979be2015-05-22 14:11:35 -07002215 private void invalidateCustomTokenLocked(
2216 UserAccounts accounts,
2217 String accountType,
2218 String authToken) {
2219 if (authToken == null || accountType == null) {
2220 return;
2221 }
2222 // Also wipe out cached token in memory.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002223 accounts.accountTokenCaches.remove(accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002224 }
2225
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002226 private void invalidateAuthTokenLocked(UserAccounts accounts, String accountType,
2227 String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002228 if (authToken == null || accountType == null) {
2229 return;
2230 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002231 Cursor cursor = accounts.accountsDb.findAuthtokenForAllAccounts(accountType, authToken);
Fred Quintana33269202009-04-20 16:05:10 -07002232 try {
2233 while (cursor.moveToNext()) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002234 String authTokenId = cursor.getString(0);
Fred Quintana33269202009-04-20 16:05:10 -07002235 String accountName = cursor.getString(1);
2236 String authTokenType = cursor.getString(2);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002237 accounts.accountsDb.deleteAuthToken(authTokenId);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002238 writeAuthTokenIntoCacheLocked(
2239 accounts,
Carlos Valdivia91979be2015-05-22 14:11:35 -07002240 new Account(accountName, accountType),
2241 authTokenType,
2242 null);
Fred Quintana60307342009-03-24 22:48:12 -07002243 }
Fred Quintana33269202009-04-20 16:05:10 -07002244 } finally {
2245 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07002246 }
2247 }
2248
Carlos Valdivia91979be2015-05-22 14:11:35 -07002249 private void saveCachedToken(
2250 UserAccounts accounts,
2251 Account account,
2252 String callerPkg,
2253 byte[] callerSigDigest,
2254 String tokenType,
2255 String token,
2256 long expiryMillis) {
2257
2258 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
2259 return;
2260 }
2261 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002262 UserHandle.of(accounts.userId));
Carlos Valdivia91979be2015-05-22 14:11:35 -07002263 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002264 accounts.accountTokenCaches.put(
2265 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002266 }
2267 }
2268
Amith Yamasani04e0d262012-02-14 11:50:53 -08002269 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
2270 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07002271 if (account == null || type == null) {
2272 return false;
2273 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002274 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002275 UserHandle.of(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002276 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002277 accounts.accountsDb.beginTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002278 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002279 long accountId = accounts.accountsDb.findDeAccountId(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002280 if (accountId < 0) {
2281 return false;
2282 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002283 accounts.accountsDb.deleteAuthtokensByAccountIdAndType(accountId, type);
2284 if (accounts.accountsDb.insertAuthToken(accountId, type, authToken) >= 0) {
2285 accounts.accountsDb.setTransactionSuccessful();
2286 writeAuthTokenIntoCacheLocked(accounts, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002287 return true;
2288 }
Fred Quintana33269202009-04-20 16:05:10 -07002289 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002290 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002291 accounts.accountsDb.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07002292 }
Fred Quintana60307342009-03-24 22:48:12 -07002293 }
2294 }
2295
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002296 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002297 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002298 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002299 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2300 Log.v(TAG, "peekAuthToken: " + account
2301 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002302 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002303 + ", pid " + Binder.getCallingPid());
2304 }
Fred Quintana382601f2010-03-25 12:25:10 -07002305 if (account == null) throw new IllegalArgumentException("account is null");
2306 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002307 int userId = UserHandle.getCallingUserId();
2308 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002309 String msg = String.format(
2310 "uid %s cannot peek the authtokens associated with accounts of type: %s",
2311 callingUid,
2312 account.type);
2313 throw new SecurityException(msg);
2314 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002315 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07002316 Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
2317 + callingUid);
2318 return null;
2319 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002320 long identityToken = clearCallingIdentity();
2321 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002322 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002323 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002324 } finally {
2325 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002326 }
Fred Quintana60307342009-03-24 22:48:12 -07002327 }
2328
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002329 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002330 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002331 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002332 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2333 Log.v(TAG, "setAuthToken: " + account
2334 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002335 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002336 + ", pid " + Binder.getCallingPid());
2337 }
Fred Quintana382601f2010-03-25 12:25:10 -07002338 if (account == null) throw new IllegalArgumentException("account is null");
2339 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002340 int userId = UserHandle.getCallingUserId();
2341 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002342 String msg = String.format(
2343 "uid %s cannot set auth tokens associated with accounts of type: %s",
2344 callingUid,
2345 account.type);
2346 throw new SecurityException(msg);
2347 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002348 long identityToken = clearCallingIdentity();
2349 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002350 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002351 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002352 } finally {
2353 restoreCallingIdentity(identityToken);
2354 }
Fred Quintana60307342009-03-24 22:48:12 -07002355 }
2356
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002357 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002358 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002359 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002360 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2361 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002362 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002363 + ", pid " + Binder.getCallingPid());
2364 }
Fred Quintana382601f2010-03-25 12:25:10 -07002365 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002366 int userId = UserHandle.getCallingUserId();
2367 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002368 String msg = String.format(
2369 "uid %s cannot set secrets for accounts of type: %s",
2370 callingUid,
2371 account.type);
2372 throw new SecurityException(msg);
2373 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002374 long identityToken = clearCallingIdentity();
2375 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002376 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002377 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002378 } finally {
2379 restoreCallingIdentity(identityToken);
2380 }
Fred Quintana60307342009-03-24 22:48:12 -07002381 }
2382
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002383 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
2384 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07002385 if (account == null) {
2386 return;
2387 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002388 boolean isChanged = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002389 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002390 accounts.accountsDb.beginTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002391 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002392 final long accountId = accounts.accountsDb.findDeAccountId(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002393 if (accountId >= 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002394 accounts.accountsDb.updateCeAccountPassword(accountId, password);
2395 accounts.accountsDb.deleteAuthTokensByAccountId(accountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002396 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002397 accounts.accountTokenCaches.remove(account);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002398 accounts.accountsDb.setTransactionSuccessful();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002399 // If there is an account whose password will be updated and the database
2400 // transactions succeed, then we say that a change has occured. Even if the
2401 // new password is the same as the old and there were no authtokens to delete.
2402 isChanged = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002403 String action = (password == null || password.length() == 0) ?
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002404 AccountsDb.DEBUG_ACTION_CLEAR_PASSWORD
2405 : AccountsDb.DEBUG_ACTION_SET_PASSWORD;
2406 logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08002407 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002408 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002409 accounts.accountsDb.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002410 if (isChanged) {
2411 // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
2412 sendAccountsChangedBroadcast(accounts.userId);
2413 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002414 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002415 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07002416 }
2417
Amith Yamasani04e0d262012-02-14 11:50:53 -08002418 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08002419 Log.i(TAG, "the accounts changed, sending broadcast of "
2420 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07002421 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07002422 }
2423
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002424 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002425 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002426 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002427 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2428 Log.v(TAG, "clearPassword: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002429 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002430 + ", pid " + Binder.getCallingPid());
2431 }
Fred Quintana382601f2010-03-25 12:25:10 -07002432 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002433 int userId = UserHandle.getCallingUserId();
2434 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002435 String msg = String.format(
2436 "uid %s cannot clear passwords for accounts of type: %s",
2437 callingUid,
2438 account.type);
2439 throw new SecurityException(msg);
2440 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002441 long identityToken = clearCallingIdentity();
2442 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002443 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002444 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002445 } finally {
2446 restoreCallingIdentity(identityToken);
2447 }
Fred Quintana60307342009-03-24 22:48:12 -07002448 }
2449
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002450 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002451 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002452 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002453 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2454 Log.v(TAG, "setUserData: " + account
2455 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002456 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002457 + ", pid " + Binder.getCallingPid());
2458 }
Fred Quintana382601f2010-03-25 12:25:10 -07002459 if (key == null) throw new IllegalArgumentException("key is null");
2460 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002461 int userId = UserHandle.getCallingUserId();
2462 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002463 String msg = String.format(
2464 "uid %s cannot set user data for accounts of type: %s",
2465 callingUid,
2466 account.type);
2467 throw new SecurityException(msg);
2468 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002469 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002470 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002471 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002472 synchronized (accounts.cacheLock) {
2473 if (!accountExistsCacheLocked(accounts, account)) {
2474 return;
2475 }
2476 setUserdataInternalLocked(accounts, account, key, value);
2477 }
Fred Quintana60307342009-03-24 22:48:12 -07002478 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002479 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002480 }
2481 }
2482
Simranjit Kohli858511c2016-03-10 18:36:11 +00002483 private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
2484 if (accounts.accountCache.containsKey(account.type)) {
2485 for (Account acc : accounts.accountCache.get(account.type)) {
2486 if (acc.name.equals(account.name)) {
2487 return true;
2488 }
2489 }
2490 }
2491 return false;
2492 }
2493
2494 private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
Amith Yamasani04e0d262012-02-14 11:50:53 -08002495 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07002496 if (account == null || key == null) {
2497 return;
2498 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002499 accounts.accountsDb.beginTransaction();
Simranjit Kohli858511c2016-03-10 18:36:11 +00002500 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002501 long accountId = accounts.accountsDb.findDeAccountId(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002502 if (accountId < 0) {
2503 return;
2504 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002505 long extrasId = accounts.accountsDb.findExtrasIdByAccountId(accountId, key);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002506 if (extrasId < 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002507 extrasId = accounts.accountsDb.insertExtra(accountId, key, value);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002508 if (extrasId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002509 return;
2510 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002511 } else if (!accounts.accountsDb.updateExtra(extrasId, value)) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002512 return;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002513 }
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002514 writeUserDataIntoCacheLocked(accounts, account, key, value);
2515 accounts.accountsDb.setTransactionSuccessful();
Simranjit Kohli858511c2016-03-10 18:36:11 +00002516 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07002517 accounts.accountsDb.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002518 }
2519 }
2520
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002521 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08002522 if (result == null) {
2523 Log.e(TAG, "the result is unexpectedly null", new Exception());
2524 }
2525 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2526 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2527 + response);
2528 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002529 try {
2530 response.onResult(result);
2531 } catch (RemoteException e) {
2532 // if the caller is dead then there is no one to care about remote
2533 // exceptions
2534 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2535 Log.v(TAG, "failure while notifying response", e);
2536 }
2537 }
2538 }
2539
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002540 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07002541 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
2542 final String authTokenType)
2543 throws RemoteException {
2544 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08002545 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2546
Fred Quintanad9640ec2012-05-23 12:37:00 -07002547 final int callingUid = getCallingUid();
2548 clearCallingIdentity();
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07002549 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07002550 throw new SecurityException("can only call from system");
2551 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002552 int userId = UserHandle.getUserId(callingUid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002553 long identityToken = clearCallingIdentity();
2554 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002555 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002556 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
2557 false /* stripAuthTokenFromResult */, null /* accountName */,
2558 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002559 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002560 protected String toDebugString(long now) {
2561 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07002562 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08002563 + ", authTokenType " + authTokenType;
2564 }
2565
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002566 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002567 public void run() throws RemoteException {
2568 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2569 }
2570
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002571 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002572 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002573 Bundle.setDefusable(result, true);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002574 if (result != null) {
2575 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
2576 Bundle bundle = new Bundle();
2577 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
2578 super.onResult(bundle);
2579 return;
2580 } else {
2581 super.onResult(result);
2582 }
2583 }
2584 }.bind();
2585 } finally {
2586 restoreCallingIdentity(identityToken);
2587 }
2588 }
2589
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002590 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07002591 public void getAuthToken(
2592 IAccountManagerResponse response,
2593 final Account account,
2594 final String authTokenType,
2595 final boolean notifyOnAuthFailure,
2596 final boolean expectActivityLaunch,
2597 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002598 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002599 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2600 Log.v(TAG, "getAuthToken: " + account
2601 + ", response " + response
2602 + ", authTokenType " + authTokenType
2603 + ", notifyOnAuthFailure " + notifyOnAuthFailure
2604 + ", expectActivityLaunch " + expectActivityLaunch
2605 + ", caller's uid " + Binder.getCallingUid()
2606 + ", pid " + Binder.getCallingPid());
2607 }
Fred Quintana382601f2010-03-25 12:25:10 -07002608 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002609 try {
2610 if (account == null) {
2611 Slog.w(TAG, "getAuthToken called with null account");
2612 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
2613 return;
2614 }
2615 if (authTokenType == null) {
2616 Slog.w(TAG, "getAuthToken called with null authTokenType");
2617 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
2618 return;
2619 }
2620 } catch (RemoteException e) {
2621 Slog.w(TAG, "Failed to report error back to the client." + e);
2622 return;
2623 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002624 int userId = UserHandle.getCallingUserId();
2625 long ident = Binder.clearCallingIdentity();
2626 final UserAccounts accounts;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002627 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002628 try {
2629 accounts = getUserAccounts(userId);
2630 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2631 AuthenticatorDescription.newKey(account.type), accounts.userId);
2632 } finally {
2633 Binder.restoreCallingIdentity(ident);
2634 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002635
Costin Manolachea40c6302010-12-13 14:50:45 -08002636 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07002637 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08002638
2639 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002640 final int callerUid = Binder.getCallingUid();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002641 final boolean permissionGranted =
2642 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08002643
Carlos Valdivia91979be2015-05-22 14:11:35 -07002644 // Get the calling package. We will use it for the purpose of caching.
2645 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07002646 List<String> callerOwnedPackageNames;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002647 ident = Binder.clearCallingIdentity();
Amith Yamasanie7360012015-06-03 17:39:40 -07002648 try {
2649 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
2650 } finally {
2651 Binder.restoreCallingIdentity(ident);
2652 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002653 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
2654 String msg = String.format(
2655 "Uid %s is attempting to illegally masquerade as package %s!",
2656 callerUid,
2657 callerPkg);
2658 throw new SecurityException(msg);
2659 }
2660
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002661 // let authenticator know the identity of the caller
2662 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
2663 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07002664
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002665 if (notifyOnAuthFailure) {
2666 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08002667 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002668
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002669 long identityToken = clearCallingIdentity();
2670 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07002671 // Distill the caller's package signatures into a single digest.
2672 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
2673
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002674 // if the caller has permission, do the peek. otherwise go the more expensive
2675 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08002676 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002677 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002678 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002679 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002680 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
2681 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2682 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002683 onResult(response, result);
2684 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002685 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002686 }
2687
Carlos Valdivia91979be2015-05-22 14:11:35 -07002688 if (customTokens) {
2689 /*
2690 * Look up tokens in the new cache only if the loginOptions don't have parameters
2691 * outside of those expected to be injected by the AccountManager, e.g.
2692 * ANDORID_PACKAGE_NAME.
2693 */
2694 String token = readCachedTokenInternal(
2695 accounts,
2696 account,
2697 authTokenType,
2698 callerPkg,
2699 callerPkgSigDigest);
2700 if (token != null) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002701 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2702 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2703 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002704 Bundle result = new Bundle();
2705 result.putString(AccountManager.KEY_AUTHTOKEN, token);
2706 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2707 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2708 onResult(response, result);
2709 return;
2710 }
2711 }
2712
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002713 new Session(
2714 accounts,
2715 response,
2716 account.type,
2717 expectActivityLaunch,
2718 false /* stripAuthTokenFromResult */,
2719 account.name,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002720 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002721 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002722 protected String toDebugString(long now) {
2723 if (loginOptions != null) loginOptions.keySet();
2724 return super.toDebugString(now) + ", getAuthToken"
2725 + ", " + account
2726 + ", authTokenType " + authTokenType
2727 + ", loginOptions " + loginOptions
2728 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2729 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002730
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002731 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002732 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002733 // If the caller doesn't have permission then create and return the
2734 // "grant permission" intent instead of the "getAuthToken" intent.
2735 if (!permissionGranted) {
2736 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2737 } else {
2738 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2739 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002740 }
2741
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002742 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002743 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002744 Bundle.setDefusable(result, true);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002745 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002746 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002747 Intent intent = newGrantCredentialsPermissionIntent(
2748 account,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002749 null,
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002750 callerUid,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002751 new AccountAuthenticatorResponse(this),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002752 authTokenType,
2753 true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002754 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002755 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002756 onResult(bundle);
2757 return;
2758 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002759 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002760 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002761 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2762 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002763 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002764 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002765 "the type and name should not be empty");
2766 return;
2767 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002768 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08002769 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002770 saveAuthTokenToDatabase(
2771 mAccounts,
2772 resultAccount,
2773 authTokenType,
2774 authToken);
2775 }
2776 long expiryMillis = result.getLong(
2777 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2778 if (customTokens
2779 && expiryMillis > System.currentTimeMillis()) {
2780 saveCachedToken(
2781 mAccounts,
2782 account,
2783 callerPkg,
2784 callerPkgSigDigest,
2785 authTokenType,
2786 authToken,
2787 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08002788 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002789 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002790
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002791 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08002792 if (intent != null && notifyOnAuthFailure && !customTokens) {
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002793 /*
2794 * Make sure that the supplied intent is owned by the authenticator
2795 * giving it to the system. Otherwise a malicious authenticator could
2796 * have users launching arbitrary activities by tricking users to
2797 * interact with malicious notifications.
2798 */
2799 checkKeyIntent(
2800 Binder.getCallingUid(),
2801 intent);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002802 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002803 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002804 intent, "android", accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002805 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002806 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002807 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07002808 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002809 }.bind();
2810 } finally {
2811 restoreCallingIdentity(identityToken);
2812 }
Fred Quintana60307342009-03-24 22:48:12 -07002813 }
2814
Carlos Valdivia91979be2015-05-22 14:11:35 -07002815 private byte[] calculatePackageSignatureDigest(String callerPkg) {
2816 MessageDigest digester;
2817 try {
2818 digester = MessageDigest.getInstance("SHA-256");
2819 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2820 callerPkg, PackageManager.GET_SIGNATURES);
2821 for (Signature sig : pkgInfo.signatures) {
2822 digester.update(sig.toByteArray());
2823 }
2824 } catch (NoSuchAlgorithmException x) {
2825 Log.wtf(TAG, "SHA-256 should be available", x);
2826 digester = null;
2827 } catch (NameNotFoundException e) {
2828 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2829 digester = null;
2830 }
2831 return (digester == null) ? null : digester.digest();
2832 }
2833
Dianne Hackborn41203752012-08-31 14:05:51 -07002834 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002835 String packageName, int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002836 int uid = intent.getIntExtra(
2837 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2838 String authTokenType = intent.getStringExtra(
2839 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
Eric Fischeree452ee2009-08-31 17:58:06 -07002840 final String titleAndSubtitle =
2841 mContext.getString(R.string.permission_request_notification_with_subtitle,
2842 account.name);
2843 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07002844 String title = titleAndSubtitle;
2845 String subtitle = "";
2846 if (index > 0) {
2847 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002848 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07002849 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002850 UserHandle user = UserHandle.of(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01002851 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04002852 Notification n = new Notification.Builder(contextForUser)
2853 .setSmallIcon(android.R.drawable.stat_sys_warning)
2854 .setWhen(0)
2855 .setColor(contextForUser.getColor(
2856 com.android.internal.R.color.system_notification_accent_color))
2857 .setContentTitle(title)
2858 .setContentText(subtitle)
2859 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2860 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2861 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002862 installNotification(getCredentialPermissionNotificationId(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002863 account, authTokenType, uid), n, packageName, user.getIdentifier());
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002864 }
2865
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002866 private Intent newGrantCredentialsPermissionIntent(Account account, String packageName,
2867 int uid, AccountAuthenticatorResponse response, String authTokenType,
2868 boolean startInNewTask) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002869
2870 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002871
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002872 if (startInNewTask) {
2873 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
2874 // Since it was set in Eclair+ we can't change it without breaking apps using
2875 // the intent from a non-Activity context. This is the default behavior.
2876 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2877 }
2878 intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account,
2879 authTokenType, uid) + (packageName != null ? packageName : "")));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002880 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002881 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2882 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002883 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002884
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002885 return intent;
2886 }
2887
2888 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2889 int uid) {
2890 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002891 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002892 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002893 final Pair<Pair<Account, String>, Integer> key =
2894 new Pair<Pair<Account, String>, Integer>(
2895 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002896 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002897 if (id == null) {
2898 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002899 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002900 }
2901 }
2902 return id;
2903 }
2904
Amith Yamasani04e0d262012-02-14 11:50:53 -08002905 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002906 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002907 synchronized (accounts.signinRequiredNotificationIds) {
2908 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002909 if (id == null) {
2910 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002911 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002912 }
2913 }
2914 return id;
2915 }
2916
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002917 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07002918 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07002919 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002920 final boolean expectActivityLaunch, final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002921 Bundle.setDefusable(optionsIn, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002922 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2923 Log.v(TAG, "addAccount: accountType " + accountType
2924 + ", response " + response
2925 + ", authTokenType " + authTokenType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002926 + ", requiredFeatures " + Arrays.toString(requiredFeatures)
Fred Quintana56285a62010-12-02 14:20:51 -08002927 + ", expectActivityLaunch " + expectActivityLaunch
2928 + ", caller's uid " + Binder.getCallingUid()
2929 + ", pid " + Binder.getCallingPid());
2930 }
Fred Quintana382601f2010-03-25 12:25:10 -07002931 if (response == null) throw new IllegalArgumentException("response is null");
2932 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002933
Amith Yamasani71e6c692013-03-24 17:39:28 -07002934 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002935 final int uid = Binder.getCallingUid();
2936 final int userId = UserHandle.getUserId(uid);
2937 if (!canUserModifyAccounts(userId, uid)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002938 try {
2939 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2940 "User is not allowed to add an account!");
2941 } catch (RemoteException re) {
2942 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002943 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002944 return;
2945 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002946 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002947 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002948 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2949 "User cannot modify accounts of this type (policy).");
2950 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002951 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002952 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2953 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002954 return;
2955 }
2956
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002957 final int pid = Binder.getCallingPid();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002958 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2959 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2960 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2961
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002962 int usrId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002963 long identityToken = clearCallingIdentity();
2964 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002965 UserAccounts accounts = getUserAccounts(usrId);
2966 logRecordWithUid(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002967 accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
2968 uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002969 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002970 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002971 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002972 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002973 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07002974 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07002975 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002976 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002977
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002978 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002979 protected String toDebugString(long now) {
2980 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07002981 + ", accountType " + accountType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002982 + ", requiredFeatures " + Arrays.toString(requiredFeatures);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002983 }
2984 }.bind();
2985 } finally {
2986 restoreCallingIdentity(identityToken);
2987 }
Fred Quintana60307342009-03-24 22:48:12 -07002988 }
2989
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002990 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002991 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
2992 final String authTokenType, final String[] requiredFeatures,
2993 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002994 Bundle.setDefusable(optionsIn, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002995 int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002996 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2997 Log.v(TAG, "addAccount: accountType " + accountType
2998 + ", response " + response
2999 + ", authTokenType " + authTokenType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003000 + ", requiredFeatures " + Arrays.toString(requiredFeatures)
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003001 + ", expectActivityLaunch " + expectActivityLaunch
3002 + ", caller's uid " + Binder.getCallingUid()
3003 + ", pid " + Binder.getCallingPid()
3004 + ", for user id " + userId);
3005 }
3006 if (response == null) throw new IllegalArgumentException("response is null");
3007 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003008 // Only allow the system process to add accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003009 if (isCrossUser(callingUid, userId)) {
3010 throw new SecurityException(
3011 String.format(
3012 "User %s trying to add account for %s" ,
3013 UserHandle.getCallingUserId(),
3014 userId));
3015 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003016
3017 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003018 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003019 try {
3020 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3021 "User is not allowed to add an account!");
3022 } catch (RemoteException re) {
3023 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003024 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003025 return;
3026 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003027 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003028 try {
3029 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3030 "User cannot modify accounts of this type (policy).");
3031 } catch (RemoteException re) {
3032 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003033 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3034 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003035 return;
3036 }
3037
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003038 final int pid = Binder.getCallingPid();
3039 final int uid = Binder.getCallingUid();
3040 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3041 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3042 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3043
3044 long identityToken = clearCallingIdentity();
3045 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003046 UserAccounts accounts = getUserAccounts(userId);
3047 logRecordWithUid(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003048 accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
3049 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003050 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003051 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003052 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003053 @Override
3054 public void run() throws RemoteException {
3055 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
3056 options);
3057 }
3058
3059 @Override
3060 protected String toDebugString(long now) {
3061 return super.toDebugString(now) + ", addAccount"
3062 + ", accountType " + accountType
3063 + ", requiredFeatures "
3064 + (requiredFeatures != null
3065 ? TextUtils.join(",", requiredFeatures)
3066 : null);
3067 }
3068 }.bind();
3069 } finally {
3070 restoreCallingIdentity(identityToken);
3071 }
3072 }
3073
Sandra Kwan78812282015-11-04 11:19:47 -08003074 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003075 public void startAddAccountSession(
3076 final IAccountManagerResponse response,
3077 final String accountType,
3078 final String authTokenType,
3079 final String[] requiredFeatures,
Sandra Kwan78812282015-11-04 11:19:47 -08003080 final boolean expectActivityLaunch,
3081 final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003082 Bundle.setDefusable(optionsIn, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003083 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3084 Log.v(TAG,
3085 "startAddAccountSession: accountType " + accountType
3086 + ", response " + response
3087 + ", authTokenType " + authTokenType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003088 + ", requiredFeatures " + Arrays.toString(requiredFeatures)
Sandra Kwan78812282015-11-04 11:19:47 -08003089 + ", expectActivityLaunch " + expectActivityLaunch
3090 + ", caller's uid " + Binder.getCallingUid()
3091 + ", pid " + Binder.getCallingPid());
3092 }
3093 if (response == null) {
3094 throw new IllegalArgumentException("response is null");
3095 }
3096 if (accountType == null) {
3097 throw new IllegalArgumentException("accountType is null");
3098 }
3099
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003100 final int uid = Binder.getCallingUid();
3101 final int userId = UserHandle.getUserId(uid);
3102 if (!canUserModifyAccounts(userId, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003103 try {
3104 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3105 "User is not allowed to add an account!");
3106 } catch (RemoteException re) {
3107 }
3108 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3109 return;
3110 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003111 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003112 try {
3113 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3114 "User cannot modify accounts of this type (policy).");
3115 } catch (RemoteException re) {
3116 }
3117 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3118 userId);
3119 return;
3120 }
Sandra Kwan78812282015-11-04 11:19:47 -08003121 final int pid = Binder.getCallingPid();
Sandra Kwan78812282015-11-04 11:19:47 -08003122 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3123 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3124 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3125
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003126 // Check to see if the Password should be included to the caller.
3127 String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3128 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003129 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003130
Sandra Kwan78812282015-11-04 11:19:47 -08003131 long identityToken = clearCallingIdentity();
3132 try {
Hongming Jin368aa192016-07-29 14:29:54 -07003133 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003134 logRecordWithUid(accounts, AccountsDb.DEBUG_ACTION_CALLED_START_ACCOUNT_ADD,
3135 AccountsDb.TABLE_ACCOUNTS, uid);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003136 new StartAccountSession(
3137 accounts,
3138 response,
3139 accountType,
3140 expectActivityLaunch,
3141 null /* accountName */,
3142 false /* authDetailsRequired */,
3143 true /* updateLastAuthenticationTime */,
3144 isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003145 @Override
3146 public void run() throws RemoteException {
3147 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
3148 requiredFeatures, options);
3149 }
3150
3151 @Override
3152 protected String toDebugString(long now) {
3153 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
3154 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
3155 + accountType + ", requiredFeatures "
3156 + (requiredFeatures != null ? requiredFeaturesStr : null);
3157 }
3158 }.bind();
3159 } finally {
3160 restoreCallingIdentity(identityToken);
3161 }
3162 }
3163
3164 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
3165 private abstract class StartAccountSession extends Session {
3166
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003167 private final boolean mIsPasswordForwardingAllowed;
3168
3169 public StartAccountSession(
3170 UserAccounts accounts,
3171 IAccountManagerResponse response,
3172 String accountType,
3173 boolean expectActivityLaunch,
3174 String accountName,
3175 boolean authDetailsRequired,
3176 boolean updateLastAuthenticationTime,
3177 boolean isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003178 super(accounts, response, accountType, expectActivityLaunch,
3179 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
3180 updateLastAuthenticationTime);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003181 mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
Sandra Kwan78812282015-11-04 11:19:47 -08003182 }
3183
3184 @Override
3185 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003186 Bundle.setDefusable(result, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003187 mNumResults++;
3188 Intent intent = null;
Sandra Kwan78812282015-11-04 11:19:47 -08003189 if (result != null
3190 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08003191 checkKeyIntent(
3192 Binder.getCallingUid(),
3193 intent);
Sandra Kwan78812282015-11-04 11:19:47 -08003194 }
Sandra Kwan78812282015-11-04 11:19:47 -08003195 IAccountManagerResponse response;
3196 if (mExpectActivityLaunch && result != null
3197 && result.containsKey(AccountManager.KEY_INTENT)) {
3198 response = mResponse;
3199 } else {
3200 response = getResponseAndClose();
3201 }
3202 if (response == null) {
3203 return;
3204 }
3205 if (result == null) {
3206 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3207 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
3208 + response);
3209 }
3210 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3211 "null bundle returned");
3212 return;
3213 }
3214
3215 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
3216 // All AccountManager error codes are greater
3217 // than 0
3218 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
3219 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3220 return;
3221 }
3222
Hongming Jin368aa192016-07-29 14:29:54 -07003223 // Omit passwords if the caller isn't permitted to see them.
3224 if (!mIsPasswordForwardingAllowed) {
3225 result.remove(AccountManager.KEY_PASSWORD);
3226 }
3227
Sandra Kwan78812282015-11-04 11:19:47 -08003228 // Strip auth token from result.
3229 result.remove(AccountManager.KEY_AUTHTOKEN);
3230
3231 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3232 Log.v(TAG,
3233 getClass().getSimpleName() + " calling onResult() on response " + response);
3234 }
3235
3236 // Get the session bundle created by authenticator. The
3237 // bundle contains data necessary for finishing the session
3238 // later. The session bundle will be encrypted here and
3239 // decrypted later when trying to finish the session.
3240 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
3241 if (sessionBundle != null) {
3242 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3243 if (TextUtils.isEmpty(accountType)
Andreas Gampe9b041742015-12-11 17:23:33 -08003244 || !mAccountType.equalsIgnoreCase(accountType)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003245 Log.w(TAG, "Account type in session bundle doesn't match request.");
3246 }
3247 // Add accountType info to session bundle. This will
3248 // override any value set by authenticator.
3249 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
3250
3251 // Encrypt session bundle before returning to caller.
3252 try {
3253 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3254 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
3255 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
3256 } catch (GeneralSecurityException e) {
3257 if (Log.isLoggable(TAG, Log.DEBUG)) {
3258 Log.v(TAG, "Failed to encrypt session bundle!", e);
3259 }
3260 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3261 "failed to encrypt session bundle");
3262 return;
3263 }
3264 }
3265
3266 sendResponse(response, result);
3267 }
3268 }
3269
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003270 @Override
Sandra Kwan0b84b452016-01-20 15:25:42 -08003271 public void finishSessionAsUser(IAccountManagerResponse response,
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003272 @NonNull Bundle sessionBundle,
3273 boolean expectActivityLaunch,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003274 Bundle appInfo,
3275 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003276 Bundle.setDefusable(sessionBundle, true);
Sandra Kwan0b84b452016-01-20 15:25:42 -08003277 int callingUid = Binder.getCallingUid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003278 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3279 Log.v(TAG,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003280 "finishSession: response "+ response
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003281 + ", expectActivityLaunch " + expectActivityLaunch
Sandra Kwan0b84b452016-01-20 15:25:42 -08003282 + ", caller's uid " + callingUid
3283 + ", caller's user id " + UserHandle.getCallingUserId()
3284 + ", pid " + Binder.getCallingPid()
3285 + ", for user id " + userId);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003286 }
3287 if (response == null) {
3288 throw new IllegalArgumentException("response is null");
3289 }
3290
3291 // Session bundle is the encrypted bundle of the original bundle created by authenticator.
3292 // Account type is added to it before encryption.
3293 if (sessionBundle == null || sessionBundle.size() == 0) {
3294 throw new IllegalArgumentException("sessionBundle is empty");
3295 }
3296
Sandra Kwan0b84b452016-01-20 15:25:42 -08003297 // Only allow the system process to finish session for other users
3298 if (isCrossUser(callingUid, userId)) {
3299 throw new SecurityException(
3300 String.format(
3301 "User %s trying to finish session for %s without cross user permission",
3302 UserHandle.getCallingUserId(),
3303 userId));
3304 }
3305
Sandra Kwan0b84b452016-01-20 15:25:42 -08003306 if (!canUserModifyAccounts(userId, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003307 sendErrorResponse(response,
3308 AccountManager.ERROR_CODE_USER_RESTRICTED,
3309 "User is not allowed to add an account!");
3310 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3311 return;
3312 }
3313
3314 final int pid = Binder.getCallingPid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003315 final Bundle decryptedBundle;
3316 final String accountType;
3317 // First decrypt session bundle to get account type for checking permission.
3318 try {
3319 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3320 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
3321 if (decryptedBundle == null) {
3322 sendErrorResponse(
3323 response,
3324 AccountManager.ERROR_CODE_BAD_REQUEST,
3325 "failed to decrypt session bundle");
3326 return;
3327 }
3328 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3329 // Account type cannot be null. This should not happen if session bundle was created
3330 // properly by #StartAccountSession.
3331 if (TextUtils.isEmpty(accountType)) {
3332 sendErrorResponse(
3333 response,
3334 AccountManager.ERROR_CODE_BAD_ARGUMENTS,
3335 "accountType is empty");
3336 return;
3337 }
3338
3339 // If by any chances, decryptedBundle contains colliding keys with
3340 // system info
3341 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
3342 // update credentials flow, we should replace with the new values of the current call.
3343 if (appInfo != null) {
3344 decryptedBundle.putAll(appInfo);
3345 }
3346
3347 // Add info that may be used by add account or update credentials flow.
Sandra Kwan0b84b452016-01-20 15:25:42 -08003348 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003349 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
3350 } catch (GeneralSecurityException e) {
3351 if (Log.isLoggable(TAG, Log.DEBUG)) {
3352 Log.v(TAG, "Failed to decrypt session bundle!", e);
3353 }
3354 sendErrorResponse(
3355 response,
3356 AccountManager.ERROR_CODE_BAD_REQUEST,
3357 "failed to decrypt session bundle");
3358 return;
3359 }
3360
Sandra Kwan0b84b452016-01-20 15:25:42 -08003361 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003362 sendErrorResponse(
3363 response,
3364 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3365 "User cannot modify accounts of this type (policy).");
3366 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3367 userId);
3368 return;
3369 }
3370
3371 long identityToken = clearCallingIdentity();
3372 try {
3373 UserAccounts accounts = getUserAccounts(userId);
3374 logRecordWithUid(
3375 accounts,
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003376 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_SESSION_FINISH,
3377 AccountsDb.TABLE_ACCOUNTS,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003378 callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003379 new Session(
3380 accounts,
3381 response,
3382 accountType,
3383 expectActivityLaunch,
3384 true /* stripAuthTokenFromResult */,
3385 null /* accountName */,
3386 false /* authDetailsRequired */,
3387 true /* updateLastAuthenticationTime */) {
3388 @Override
3389 public void run() throws RemoteException {
3390 mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
3391 }
3392
3393 @Override
3394 protected String toDebugString(long now) {
3395 return super.toDebugString(now)
3396 + ", finishSession"
3397 + ", accountType " + accountType;
3398 }
3399 }.bind();
3400 } finally {
3401 restoreCallingIdentity(identityToken);
3402 }
3403 }
3404
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003405 private void showCantAddAccount(int errorCode, int userId) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003406 final DevicePolicyManagerInternal dpmi =
3407 LocalServices.getService(DevicePolicyManagerInternal.class);
3408 Intent intent = null;
Nicolas Prevot14fc1972016-08-24 14:21:38 +01003409 if (dpmi == null) {
3410 intent = getDefaultCantAddAccountIntent(errorCode);
3411 } else if (errorCode == AccountManager.ERROR_CODE_USER_RESTRICTED) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003412 intent = dpmi.createUserRestrictionSupportIntent(userId,
3413 UserManager.DISALLOW_MODIFY_ACCOUNTS);
3414 } else if (errorCode == AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
3415 intent = dpmi.createShowAdminSupportIntent(userId, false);
3416 }
3417 if (intent == null) {
3418 intent = getDefaultCantAddAccountIntent(errorCode);
3419 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003420 long identityToken = clearCallingIdentity();
3421 try {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003422 mContext.startActivityAsUser(intent, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003423 } finally {
3424 restoreCallingIdentity(identityToken);
3425 }
3426 }
3427
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003428 /**
3429 * Called when we don't know precisely who is preventing us from adding an account.
3430 */
3431 private Intent getDefaultCantAddAccountIntent(int errorCode) {
3432 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
3433 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
3434 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3435 return cantAddAccount;
3436 }
3437
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003438 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003439 public void confirmCredentialsAsUser(
3440 IAccountManagerResponse response,
3441 final Account account,
3442 final Bundle options,
3443 final boolean expectActivityLaunch,
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003444 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003445 Bundle.setDefusable(options, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003446 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003447 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3448 Log.v(TAG, "confirmCredentials: " + account
3449 + ", response " + response
3450 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003451 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003452 + ", pid " + Binder.getCallingPid());
3453 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003454 // Only allow the system process to read accounts of other users
3455 if (isCrossUser(callingUid, userId)) {
3456 throw new SecurityException(
3457 String.format(
3458 "User %s trying to confirm account credentials for %s" ,
3459 UserHandle.getCallingUserId(),
3460 userId));
3461 }
Fred Quintana382601f2010-03-25 12:25:10 -07003462 if (response == null) throw new IllegalArgumentException("response is null");
3463 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003464 long identityToken = clearCallingIdentity();
3465 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003466 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003467 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003468 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003469 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003470 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003471 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003472 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003473 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003474 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003475 protected String toDebugString(long now) {
3476 return super.toDebugString(now) + ", confirmCredentials"
3477 + ", " + account;
3478 }
3479 }.bind();
3480 } finally {
3481 restoreCallingIdentity(identityToken);
3482 }
Fred Quintana60307342009-03-24 22:48:12 -07003483 }
3484
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003485 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003486 public void updateCredentials(IAccountManagerResponse response, final Account account,
3487 final String authTokenType, final boolean expectActivityLaunch,
3488 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003489 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003490 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3491 Log.v(TAG, "updateCredentials: " + account
3492 + ", response " + response
3493 + ", authTokenType " + authTokenType
3494 + ", expectActivityLaunch " + expectActivityLaunch
3495 + ", caller's uid " + Binder.getCallingUid()
3496 + ", pid " + Binder.getCallingPid());
3497 }
Fred Quintana382601f2010-03-25 12:25:10 -07003498 if (response == null) throw new IllegalArgumentException("response is null");
3499 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003500 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003501 long identityToken = clearCallingIdentity();
3502 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003503 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003504 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003505 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003506 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003507 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003508 public void run() throws RemoteException {
3509 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
3510 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003511 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003512 protected String toDebugString(long now) {
3513 if (loginOptions != null) loginOptions.keySet();
3514 return super.toDebugString(now) + ", updateCredentials"
3515 + ", " + account
3516 + ", authTokenType " + authTokenType
3517 + ", loginOptions " + loginOptions;
3518 }
3519 }.bind();
3520 } finally {
3521 restoreCallingIdentity(identityToken);
3522 }
Fred Quintana60307342009-03-24 22:48:12 -07003523 }
3524
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003525 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003526 public void startUpdateCredentialsSession(
3527 IAccountManagerResponse response,
3528 final Account account,
3529 final String authTokenType,
3530 final boolean expectActivityLaunch,
3531 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003532 Bundle.setDefusable(loginOptions, true);
Sandra Kwane68c37e2015-11-12 17:11:49 -08003533 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3534 Log.v(TAG,
3535 "startUpdateCredentialsSession: " + account + ", response " + response
3536 + ", authTokenType " + authTokenType + ", expectActivityLaunch "
3537 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
3538 + ", pid " + Binder.getCallingPid());
3539 }
3540 if (response == null) {
3541 throw new IllegalArgumentException("response is null");
3542 }
3543 if (account == null) {
3544 throw new IllegalArgumentException("account is null");
3545 }
Sandra Kwana578d112015-12-16 16:01:43 -08003546
3547 final int uid = Binder.getCallingUid();
Sandra Kwane68c37e2015-11-12 17:11:49 -08003548 int userId = UserHandle.getCallingUserId();
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003549
3550 // Check to see if the Password should be included to the caller.
3551 String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3552 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003553 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003554
Sandra Kwane68c37e2015-11-12 17:11:49 -08003555 long identityToken = clearCallingIdentity();
3556 try {
3557 UserAccounts accounts = getUserAccounts(userId);
3558 new StartAccountSession(
3559 accounts,
3560 response,
3561 account.type,
3562 expectActivityLaunch,
3563 account.name,
3564 false /* authDetailsRequired */,
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003565 true /* updateLastCredentialTime */,
3566 isPasswordForwardingAllowed) {
Sandra Kwane68c37e2015-11-12 17:11:49 -08003567 @Override
3568 public void run() throws RemoteException {
3569 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
3570 loginOptions);
3571 }
3572
3573 @Override
3574 protected String toDebugString(long now) {
3575 if (loginOptions != null)
3576 loginOptions.keySet();
3577 return super.toDebugString(now)
3578 + ", startUpdateCredentialsSession"
3579 + ", " + account
3580 + ", authTokenType " + authTokenType
3581 + ", loginOptions " + loginOptions;
3582 }
3583 }.bind();
3584 } finally {
3585 restoreCallingIdentity(identityToken);
3586 }
3587 }
3588
3589 @Override
Sandra Kwan390c9d22016-01-12 14:13:37 -08003590 public void isCredentialsUpdateSuggested(
3591 IAccountManagerResponse response,
3592 final Account account,
3593 final String statusToken) {
3594 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3595 Log.v(TAG,
3596 "isCredentialsUpdateSuggested: " + account + ", response " + response
3597 + ", caller's uid " + Binder.getCallingUid()
3598 + ", pid " + Binder.getCallingPid());
3599 }
3600 if (response == null) {
3601 throw new IllegalArgumentException("response is null");
3602 }
3603 if (account == null) {
3604 throw new IllegalArgumentException("account is null");
3605 }
3606 if (TextUtils.isEmpty(statusToken)) {
3607 throw new IllegalArgumentException("status token is empty");
3608 }
3609
Sandra Kwan390c9d22016-01-12 14:13:37 -08003610 int usrId = UserHandle.getCallingUserId();
3611 long identityToken = clearCallingIdentity();
3612 try {
3613 UserAccounts accounts = getUserAccounts(usrId);
3614 new Session(accounts, response, account.type, false /* expectActivityLaunch */,
3615 false /* stripAuthTokenFromResult */, account.name,
3616 false /* authDetailsRequired */) {
3617 @Override
3618 protected String toDebugString(long now) {
3619 return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
3620 + ", " + account;
3621 }
3622
3623 @Override
3624 public void run() throws RemoteException {
3625 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
3626 }
3627
3628 @Override
3629 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003630 Bundle.setDefusable(result, true);
Sandra Kwan390c9d22016-01-12 14:13:37 -08003631 IAccountManagerResponse response = getResponseAndClose();
3632 if (response == null) {
3633 return;
3634 }
3635
3636 if (result == null) {
3637 sendErrorResponse(
3638 response,
3639 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3640 "null bundle");
3641 return;
3642 }
3643
3644 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3645 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3646 + response);
3647 }
3648 // Check to see if an error occurred. We know if an error occurred because all
3649 // error codes are greater than 0.
3650 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
3651 sendErrorResponse(response,
3652 result.getInt(AccountManager.KEY_ERROR_CODE),
3653 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3654 return;
3655 }
3656 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
3657 sendErrorResponse(
3658 response,
3659 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3660 "no result in response");
3661 return;
3662 }
3663 final Bundle newResult = new Bundle();
3664 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
3665 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
3666 sendResponse(response, newResult);
3667 }
3668 }.bind();
3669 } finally {
3670 restoreCallingIdentity(identityToken);
3671 }
3672 }
3673
3674 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003675 public void editProperties(IAccountManagerResponse response, final String accountType,
3676 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003677 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003678 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3679 Log.v(TAG, "editProperties: accountType " + accountType
3680 + ", response " + response
3681 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003682 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003683 + ", pid " + Binder.getCallingPid());
3684 }
Fred Quintana382601f2010-03-25 12:25:10 -07003685 if (response == null) throw new IllegalArgumentException("response is null");
3686 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003687 int userId = UserHandle.getCallingUserId();
3688 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003689 String msg = String.format(
3690 "uid %s cannot edit authenticator properites for account type: %s",
3691 callingUid,
3692 accountType);
3693 throw new SecurityException(msg);
3694 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003695 long identityToken = clearCallingIdentity();
3696 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003697 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003698 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003699 true /* stripAuthTokenFromResult */, null /* accountName */,
3700 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003701 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003702 public void run() throws RemoteException {
3703 mAuthenticator.editProperties(this, mAccountType);
3704 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003705 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003706 protected String toDebugString(long now) {
3707 return super.toDebugString(now) + ", editProperties"
3708 + ", accountType " + accountType;
3709 }
3710 }.bind();
3711 } finally {
3712 restoreCallingIdentity(identityToken);
3713 }
Fred Quintana60307342009-03-24 22:48:12 -07003714 }
3715
Amith Yamasani12747872015-12-07 14:19:49 -08003716 @Override
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003717 public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName,
3718 @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003719 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003720 throw new SecurityException("Can be called only by system UID");
3721 }
3722 Preconditions.checkNotNull(account, "account cannot be null");
3723 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3724 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3725
3726 final int userId = userHandle.getIdentifier();
3727
3728 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3729
3730 try {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003731 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
Svet Ganovf6d424f12016-09-20 20:18:53 -07003732 return hasAccountAccess(account, packageName, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003733 } catch (NameNotFoundException e) {
3734 return false;
3735 }
3736 }
3737
Svet Ganovf6d424f12016-09-20 20:18:53 -07003738 private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
3739 int uid) {
3740 if (packageName == null) {
3741 String[] packageNames = mPackageManager.getPackagesForUid(uid);
3742 if (ArrayUtils.isEmpty(packageNames)) {
3743 return false;
3744 }
3745 // For app op checks related to permissions all packages in the UID
3746 // have the same app op state, so doesn't matter which one we pick.
3747 packageName = packageNames[0];
3748 }
3749
3750 // Use null token which means any token. Having a token means the package
3751 // is trusted by the authenticator, hence it is fine to access the account.
3752 if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) {
3753 return true;
3754 }
3755 // In addition to the permissions required to get an auth token we also allow
3756 // the account to be accessed by holders of the get accounts permissions.
3757 return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
3758 || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
3759 }
3760
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003761 private boolean checkUidPermission(String permission, int uid, String opPackageName) {
3762 final long identity = Binder.clearCallingIdentity();
3763 try {
3764 IPackageManager pm = ActivityThread.getPackageManager();
3765 if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
3766 return false;
3767 }
3768 final int opCode = AppOpsManager.permissionToOpCode(permission);
3769 return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
3770 opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
3771 } catch (RemoteException e) {
3772 /* ignore - local call */
3773 } finally {
3774 Binder.restoreCallingIdentity(identity);
3775 }
3776 return false;
3777 }
3778
3779 @Override
3780 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
3781 @NonNull String packageName, @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003782 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003783 throw new SecurityException("Can be called only by system UID");
3784 }
3785
3786 Preconditions.checkNotNull(account, "account cannot be null");
3787 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3788 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3789
3790 final int userId = userHandle.getIdentifier();
3791
3792 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3793
3794 final int uid;
3795 try {
3796 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3797 } catch (NameNotFoundException e) {
3798 Slog.e(TAG, "Unknown package " + packageName);
3799 return null;
3800 }
3801
3802 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
3803
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003804 final long identity = Binder.clearCallingIdentity();
3805 try {
3806 return PendingIntent.getActivityAsUser(
3807 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
3808 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3809 null, new UserHandle(userId)).getIntentSender();
3810 } finally {
3811 Binder.restoreCallingIdentity(identity);
3812 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003813 }
3814
3815 private Intent newRequestAccountAccessIntent(Account account, String packageName,
3816 int uid, RemoteCallback callback) {
3817 return newGrantCredentialsPermissionIntent(account, packageName, uid,
3818 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
3819 @Override
3820 public void onResult(Bundle value) throws RemoteException {
3821 handleAuthenticatorResponse(true);
3822 }
3823
3824 @Override
3825 public void onRequestContinued() {
3826 /* ignore */
3827 }
3828
3829 @Override
3830 public void onError(int errorCode, String errorMessage) throws RemoteException {
3831 handleAuthenticatorResponse(false);
3832 }
3833
3834 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
3835 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -07003836 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003837 UserHandle.getUserHandleForUid(uid));
3838 if (callback != null) {
3839 Bundle result = new Bundle();
3840 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
3841 callback.sendResult(result);
3842 }
3843 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07003844 }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003845 }
3846
3847 @Override
Amith Yamasani12747872015-12-07 14:19:49 -08003848 public boolean someUserHasAccount(@NonNull final Account account) {
3849 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
3850 throw new SecurityException("Only system can check for accounts across users");
3851 }
3852 final long token = Binder.clearCallingIdentity();
3853 try {
3854 AccountAndUser[] allAccounts = getAllAccounts();
3855 for (int i = allAccounts.length - 1; i >= 0; i--) {
3856 if (allAccounts[i].account.equals(account)) {
3857 return true;
3858 }
3859 }
3860 return false;
3861 } finally {
3862 Binder.restoreCallingIdentity(token);
3863 }
3864 }
3865
Fred Quintana33269202009-04-20 16:05:10 -07003866 private class GetAccountsByTypeAndFeatureSession extends Session {
3867 private final String[] mFeatures;
3868 private volatile Account[] mAccountsOfType = null;
3869 private volatile ArrayList<Account> mAccountsWithFeatures = null;
3870 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003871 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07003872
Amith Yamasani04e0d262012-02-14 11:50:53 -08003873 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003874 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003875 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003876 true /* stripAuthTokenFromResult */, null /* accountName */,
3877 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003878 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07003879 mFeatures = features;
3880 }
3881
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003882 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003883 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003884 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003885 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
3886 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003887 }
Fred Quintana33269202009-04-20 16:05:10 -07003888 // check whether each account matches the requested features
Tejas Khorana5edff3b2016-06-28 20:59:52 -07003889 mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
Fred Quintana33269202009-04-20 16:05:10 -07003890 mCurrentAccount = 0;
3891
3892 checkAccount();
3893 }
3894
3895 public void checkAccount() {
3896 if (mCurrentAccount >= mAccountsOfType.length) {
3897 sendResult();
3898 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07003899 }
Fred Quintana33269202009-04-20 16:05:10 -07003900
Fred Quintana29e94b82010-03-10 12:11:51 -08003901 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
3902 if (accountAuthenticator == null) {
3903 // It is possible that the authenticator has died, which is indicated by
3904 // mAuthenticator being set to null. If this happens then just abort.
3905 // There is no need to send back a result or error in this case since
3906 // that already happened when mAuthenticator was cleared.
3907 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3908 Log.v(TAG, "checkAccount: aborting session since we are no longer"
3909 + " connected to the authenticator, " + toDebugString());
3910 }
3911 return;
3912 }
Fred Quintana33269202009-04-20 16:05:10 -07003913 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08003914 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07003915 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003916 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07003917 }
3918 }
3919
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003920 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003921 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003922 Bundle.setDefusable(result, true);
Fred Quintana33269202009-04-20 16:05:10 -07003923 mNumResults++;
3924 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003925 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07003926 return;
3927 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003928 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07003929 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3930 }
3931 mCurrentAccount++;
3932 checkAccount();
3933 }
3934
3935 public void sendResult() {
3936 IAccountManagerResponse response = getResponseAndClose();
3937 if (response != null) {
3938 try {
3939 Account[] accounts = new Account[mAccountsWithFeatures.size()];
3940 for (int i = 0; i < accounts.length; i++) {
3941 accounts[i] = mAccountsWithFeatures.get(i);
3942 }
Fred Quintana56285a62010-12-02 14:20:51 -08003943 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3944 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3945 + response);
3946 }
Fred Quintana33269202009-04-20 16:05:10 -07003947 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003948 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07003949 response.onResult(result);
3950 } catch (RemoteException e) {
3951 // if the caller is dead then there is no one to care about remote exceptions
3952 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3953 Log.v(TAG, "failure while notifying response", e);
3954 }
3955 }
3956 }
3957 }
3958
3959
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003960 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003961 protected String toDebugString(long now) {
3962 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3963 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3964 }
3965 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003966
Amith Yamasani04e0d262012-02-14 11:50:53 -08003967 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003968 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08003969 * @hide
3970 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003971 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003972 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003973 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07003974 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3975 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003976 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003977 return new Account[0];
3978 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003979 long identityToken = clearCallingIdentity();
3980 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003981 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003982 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003983 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003984 callingUid,
3985 null, // packageName
3986 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003987 } finally {
3988 restoreCallingIdentity(identityToken);
3989 }
3990 }
3991
Amith Yamasanif29f2362012-04-05 18:29:52 -07003992 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003993 * Returns accounts for all running users.
3994 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07003995 * @hide
3996 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003997 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003998 public AccountAndUser[] getRunningAccounts() {
3999 final int[] runningUserIds;
4000 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08004001 runningUserIds = ActivityManager.getService().getRunningUserIds();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004002 } catch (RemoteException e) {
4003 // Running in system_server; should never happen
4004 throw new RuntimeException(e);
4005 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004006 return getAccounts(runningUserIds);
4007 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07004008
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004009 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004010 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004011 public AccountAndUser[] getAllAccounts() {
Amith Yamasanid04aaa32016-06-13 12:09:36 -07004012 final List<UserInfo> users = getUserManager().getUsers(true);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004013 final int[] userIds = new int[users.size()];
4014 for (int i = 0; i < userIds.length; i++) {
4015 userIds[i] = users.get(i).id;
4016 }
4017 return getAccounts(userIds);
4018 }
4019
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004020 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004021 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004022 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004023 for (int userId : userIds) {
4024 UserAccounts userAccounts = getUserAccounts(userId);
4025 if (userAccounts == null) continue;
4026 synchronized (userAccounts.cacheLock) {
4027 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
4028 Binder.getCallingUid(), null);
4029 for (int a = 0; a < accounts.length; a++) {
4030 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07004031 }
4032 }
4033 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004034
4035 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
4036 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07004037 }
4038
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004039 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004040 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004041 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
4042 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004043 }
4044
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004045 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004046 private Account[] getAccountsAsUser(
4047 String type,
4048 int userId,
4049 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004050 int packageUid,
4051 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004052 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004053 // Only allow the system process to read accounts of other users
4054 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07004055 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08004056 && mContext.checkCallingOrSelfPermission(
4057 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
4058 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004059 throw new SecurityException("User " + UserHandle.getCallingUserId()
4060 + " trying to get account for " + userId);
4061 }
4062
Fred Quintana56285a62010-12-02 14:20:51 -08004063 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4064 Log.v(TAG, "getAccounts: accountType " + type
4065 + ", caller's uid " + Binder.getCallingUid()
4066 + ", pid " + Binder.getCallingPid());
4067 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004068 // If the original calling app was using the framework account chooser activity, we'll
4069 // be passed in the original caller's uid here, which is what should be used for filtering.
4070 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
4071 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07004072 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07004073 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004074 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4075 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004076 if (visibleAccountTypes.isEmpty()
4077 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004078 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004079 } else if (visibleAccountTypes.contains(type)) {
4080 // Prune the list down to just the requested type.
4081 visibleAccountTypes = new ArrayList<>();
4082 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004083 } // else aggregate all the visible accounts (it won't matter if the
4084 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004085
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004086 long identityToken = clearCallingIdentity();
4087 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004088 UserAccounts accounts = getUserAccounts(userId);
Tejas Khorana69990d92016-08-03 11:19:40 -07004089 Account[] accountsToReturn = getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004090 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004091 callingUid,
4092 callingPackage,
4093 visibleAccountTypes);
Tejas Khorana69990d92016-08-03 11:19:40 -07004094 ArrayList<Account> accountsToReturnList = new
4095 ArrayList<Account>(Arrays.asList(accountsToReturn));
4096 for(int i = accountsToReturnList.size() - 1; i >= 0 ; i--) {
4097 // if account not visible to caller or managed by caller, remove from
4098 // accounts to return. Note that all accounts visible by default unless
4099 // visible list functionality implemented
4100 if(!(isAccountVisible(accountsToReturnList.get(i), callingUid,
4101 getUserAccounts(userId)))) {
4102 accountsToReturnList.remove(i);
4103 }
4104 }
4105 return accountsToReturnList.toArray(new Account[accountsToReturnList.size()]);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004106 } finally {
4107 restoreCallingIdentity(identityToken);
4108 }
4109 }
4110
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004111 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004112 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004113 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004114 int callingUid,
4115 String callingPackage,
4116 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004117 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004118 ArrayList<Account> visibleAccounts = new ArrayList<>();
4119 for (String visibleType : visibleAccountTypes) {
4120 Account[] accountsForType = getAccountsFromCacheLocked(
4121 userAccounts, visibleType, callingUid, callingPackage);
4122 if (accountsForType != null) {
4123 visibleAccounts.addAll(Arrays.asList(accountsForType));
4124 }
4125 }
4126 Account[] result = new Account[visibleAccounts.size()];
4127 for (int i = 0; i < visibleAccounts.size(); i++) {
4128 result[i] = visibleAccounts.get(i);
4129 }
4130 return result;
4131 }
4132 }
4133
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004134 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004135 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07004136 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004137 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
4138 for (Account account : accounts) {
4139 addSharedAccountAsUser(account, userId);
4140 }
4141 }
4142
4143 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004144 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004145 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004146 accounts.accountsDb.deleteSharedAccount(account);
4147 long accountId = accounts.accountsDb.insertSharedAccount(account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004148 if (accountId < 0) {
4149 Log.w(TAG, "insertAccountIntoDatabase: " + account
4150 + ", skipping the DB insert failed");
4151 return false;
4152 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004153 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_SHARED_ACCOUNTS, accountId,
4154 accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004155 return true;
4156 }
4157
4158 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004159 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
4160 userId = handleIncomingUser(userId);
4161 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004162 long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account);
4163 int r = accounts.accountsDb.renameSharedAccount(account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004164 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004165 int callingUid = getCallingUid();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004166 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_RENAME, AccountsDb.TABLE_SHARED_ACCOUNTS,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004167 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004168 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004169 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004170 }
4171 return r > 0;
4172 }
4173
4174 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08004175 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004176 return removeSharedAccountAsUser(account, userId, getCallingUid());
4177 }
4178
4179 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004180 userId = handleIncomingUser(userId);
4181 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004182 long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account);
4183 boolean deleted = accounts.accountsDb.deleteSharedAccount(account);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004184 if (deleted) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004185 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE, AccountsDb.TABLE_SHARED_ACCOUNTS,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004186 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07004187 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004188 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004189 return deleted;
Amith Yamasani67df64b2012-12-14 12:09:36 -08004190 }
4191
4192 @Override
4193 public Account[] getSharedAccountsAsUser(int userId) {
4194 userId = handleIncomingUser(userId);
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004195 UserAccounts accounts = getUserAccounts(userId);
4196 List<Account> accountList = accounts.accountsDb.getSharedAccounts();
Amith Yamasani67df64b2012-12-14 12:09:36 -08004197 Account[] accountArray = new Account[accountList.size()];
4198 accountList.toArray(accountArray);
4199 return accountArray;
4200 }
4201
4202 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004203 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004204 public Account[] getAccounts(String type, String opPackageName) {
Tejas Khorana69990d92016-08-03 11:19:40 -07004205 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004206 }
4207
Amith Yamasani27db4682013-03-30 17:07:47 -07004208 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004209 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004210 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004211 int callingUid = Binder.getCallingUid();
4212 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
4213 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
4214 + callingUid + " with uid=" + uid);
4215 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004216 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
4217 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004218 }
4219
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004220 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004221 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004222 public Account[] getAccountsByTypeForPackage(String type, String packageName,
4223 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004224 int packageUid = -1;
4225 try {
4226 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07004227 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
4228 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004229 } catch (RemoteException re) {
4230 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
4231 return new Account[0];
4232 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004233 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
4234 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004235 }
4236
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004237 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004238 public void getAccountsByFeatures(
4239 IAccountManagerResponse response,
4240 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004241 String[] features,
4242 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004243 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08004244 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4245 Log.v(TAG, "getAccounts: accountType " + type
4246 + ", response " + response
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004247 + ", features " + Arrays.toString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004248 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08004249 + ", pid " + Binder.getCallingPid());
4250 }
Fred Quintana382601f2010-03-25 12:25:10 -07004251 if (response == null) throw new IllegalArgumentException("response is null");
4252 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004253 int userId = UserHandle.getCallingUserId();
4254
Svetoslavf3f02ac2015-09-08 14:36:35 -07004255 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4256 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004257 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004258 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004259 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004260 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
4261 try {
4262 response.onResult(result);
4263 } catch (RemoteException e) {
4264 Log.e(TAG, "Cannot respond to caller do to exception." , e);
4265 }
4266 return;
4267 }
Fred Quintana33269202009-04-20 16:05:10 -07004268 long identityToken = clearCallingIdentity();
4269 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004270 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004271 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004272 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004273 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004274 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004275 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08004276 Bundle result = new Bundle();
4277 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4278 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004279 return;
4280 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004281 new GetAccountsByTypeAndFeatureSession(
4282 userAccounts,
4283 response,
4284 type,
4285 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004286 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07004287 } finally {
4288 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07004289 }
4290 }
4291
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07004292 @Override
4293 public void onAccountAccessed(String token) throws RemoteException {
4294 final int uid = Binder.getCallingUid();
4295 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
4296 return;
4297 }
4298 final int userId = UserHandle.getCallingUserId();
4299 final long identity = Binder.clearCallingIdentity();
4300 try {
4301 for (Account account : getAccounts(userId, mContext.getOpPackageName())) {
4302 if (Objects.equals(account.getAccessId(), token)) {
4303 // An app just accessed the account. At this point it knows about
4304 // it and there is not need to hide this account from the app.
4305 if (!hasAccountAccess(account, null, uid)) {
4306 updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
4307 uid, true);
4308 }
4309 }
4310 }
4311 } finally {
4312 Binder.restoreCallingIdentity(identity);
4313 }
4314 }
4315
Fred Quintanaa698f422009-04-08 19:14:54 -07004316 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07004317 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07004318 IAccountManagerResponse mResponse;
4319 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004320 final boolean mExpectActivityLaunch;
4321 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004322 final String mAccountName;
4323 // Indicates if we need to add auth details(like last credential time)
4324 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004325 // If set, we need to update the last authenticated time. This is
4326 // currently
4327 // used on
4328 // successful confirming credentials.
4329 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07004330
Fred Quintana33269202009-04-20 16:05:10 -07004331 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07004332 private int mNumRequestContinued = 0;
4333 private int mNumErrors = 0;
4334
Fred Quintana60307342009-03-24 22:48:12 -07004335 IAccountAuthenticator mAuthenticator = null;
4336
Fred Quintana8570f742010-02-18 10:32:54 -08004337 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004338 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004339
Amith Yamasani04e0d262012-02-14 11:50:53 -08004340 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004341 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4342 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004343 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4344 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4345 }
4346
4347 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4348 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4349 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07004350 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08004351 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07004352 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08004353 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004354 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07004355 mResponse = response;
4356 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004357 mExpectActivityLaunch = expectActivityLaunch;
4358 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004359 mAccountName = accountName;
4360 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004361 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004362
Fred Quintanaa698f422009-04-08 19:14:54 -07004363 synchronized (mSessions) {
4364 mSessions.put(toString(), this);
4365 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004366 if (response != null) {
4367 try {
4368 response.asBinder().linkToDeath(this, 0 /* flags */);
4369 } catch (RemoteException e) {
4370 mResponse = null;
4371 binderDied();
4372 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004373 }
Fred Quintana60307342009-03-24 22:48:12 -07004374 }
4375
Fred Quintanaa698f422009-04-08 19:14:54 -07004376 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07004377 if (mResponse == null) {
4378 // this session has already been closed
4379 return null;
4380 }
Fred Quintana60307342009-03-24 22:48:12 -07004381 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07004382 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07004383 return response;
4384 }
4385
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004386 /**
4387 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4388 * security policy.
4389 *
4390 * In particular we want to make sure that the Authenticator doesn't try to trick users
4391 * into launching aribtrary intents on the device via by tricking to click authenticator
4392 * supplied entries in the system Settings app.
4393 */
4394 protected void checkKeyIntent(
4395 int authUid,
4396 Intent intent) throws SecurityException {
4397 long bid = Binder.clearCallingIdentity();
4398 try {
4399 PackageManager pm = mContext.getPackageManager();
4400 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4401 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4402 int targetUid = targetActivityInfo.applicationInfo.uid;
Sandra Kwan0e961a12016-06-30 14:34:01 -07004403 if (!GrantCredentialsPermissionActivity.class.getName().equals(
4404 targetActivityInfo.getClass().getName())
4405 && !CantAddAccountActivity.class
4406 .equals(targetActivityInfo.getClass().getName())
4407 && PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
4408 targetUid)) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004409 String pkgName = targetActivityInfo.packageName;
4410 String activityName = targetActivityInfo.name;
4411 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4412 + "does not share a signature with the supplying authenticator (%s).";
4413 throw new SecurityException(
4414 String.format(tmpl, activityName, pkgName, mAccountType));
4415 }
4416 } finally {
4417 Binder.restoreCallingIdentity(bid);
4418 }
4419 }
4420
Fred Quintanaa698f422009-04-08 19:14:54 -07004421 private void close() {
4422 synchronized (mSessions) {
4423 if (mSessions.remove(toString()) == null) {
4424 // the session was already closed, so bail out now
4425 return;
4426 }
4427 }
4428 if (mResponse != null) {
4429 // stop listening for response deaths
4430 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4431
4432 // clear this so that we don't accidentally send any further results
4433 mResponse = null;
4434 }
4435 cancelTimeout();
4436 unbind();
4437 }
4438
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004439 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004440 public void binderDied() {
4441 mResponse = null;
4442 close();
4443 }
4444
4445 protected String toDebugString() {
4446 return toDebugString(SystemClock.elapsedRealtime());
4447 }
4448
4449 protected String toDebugString(long now) {
4450 return "Session: expectLaunch " + mExpectActivityLaunch
4451 + ", connected " + (mAuthenticator != null)
4452 + ", stats (" + mNumResults + "/" + mNumRequestContinued
4453 + "/" + mNumErrors + ")"
4454 + ", lifetime " + ((now - mCreationTime) / 1000.0);
4455 }
4456
Fred Quintana60307342009-03-24 22:48:12 -07004457 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004458 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4459 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4460 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004461 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004462 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004463 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07004464 }
4465 }
4466
4467 private void unbind() {
4468 if (mAuthenticator != null) {
4469 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07004470 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07004471 }
4472 }
4473
Fred Quintana60307342009-03-24 22:48:12 -07004474 public void cancelTimeout() {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004475 mHandler.removeMessages(MESSAGE_TIMED_OUT, this);
Fred Quintana60307342009-03-24 22:48:12 -07004476 }
4477
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004478 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004479 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07004480 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07004481 try {
4482 run();
4483 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004484 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07004485 "remote exception");
4486 }
Fred Quintana60307342009-03-24 22:48:12 -07004487 }
4488
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004489 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004490 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004491 mAuthenticator = null;
4492 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004493 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004494 try {
4495 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4496 "disconnected");
4497 } catch (RemoteException e) {
4498 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4499 Log.v(TAG, "Session.onServiceDisconnected: "
4500 + "caught RemoteException while responding", e);
4501 }
4502 }
Fred Quintana60307342009-03-24 22:48:12 -07004503 }
4504 }
4505
Fred Quintanab839afc2009-10-14 15:57:28 -07004506 public abstract void run() throws RemoteException;
4507
Fred Quintana60307342009-03-24 22:48:12 -07004508 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004509 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004510 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004511 try {
4512 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4513 "timeout");
4514 } catch (RemoteException e) {
4515 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4516 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4517 e);
4518 }
4519 }
Fred Quintana60307342009-03-24 22:48:12 -07004520 }
4521 }
4522
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004523 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004524 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004525 Bundle.setDefusable(result, true);
Fred Quintanaa698f422009-04-08 19:14:54 -07004526 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004527 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004528 if (result != null) {
4529 boolean isSuccessfulConfirmCreds = result.getBoolean(
4530 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004531 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004532 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4533 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004534 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004535 // mUpdateLastAuthenticatedTime is true and the confirmRequest
4536 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07004537 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004538 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004539 if (needUpdate || mAuthDetailsRequired) {
4540 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4541 if (needUpdate && accountPresent) {
4542 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4543 }
4544 if (mAuthDetailsRequired) {
4545 long lastAuthenticatedTime = -1;
4546 if (accountPresent) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004547 lastAuthenticatedTime = mAccounts.accountsDb
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004548 .findAccountLastAuthenticatedTime(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004549 new Account(mAccountName, mAccountType));
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004550 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07004551 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004552 lastAuthenticatedTime);
4553 }
4554 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004555 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004556 if (result != null
4557 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004558 checkKeyIntent(
4559 Binder.getCallingUid(),
4560 intent);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004561 }
4562 if (result != null
4563 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004564 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4565 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004566 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4567 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004568 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4569 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004570 }
Fred Quintana60307342009-03-24 22:48:12 -07004571 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004572 IAccountManagerResponse response;
4573 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004574 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004575 response = mResponse;
4576 } else {
4577 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004578 }
Fred Quintana60307342009-03-24 22:48:12 -07004579 if (response != null) {
4580 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07004581 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08004582 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4583 Log.v(TAG, getClass().getSimpleName()
4584 + " calling onError() on response " + response);
4585 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004586 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07004587 "null bundle returned");
4588 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08004589 if (mStripAuthTokenFromResult) {
4590 result.remove(AccountManager.KEY_AUTHTOKEN);
4591 }
Fred Quintana56285a62010-12-02 14:20:51 -08004592 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4593 Log.v(TAG, getClass().getSimpleName()
4594 + " calling onResult() on response " + response);
4595 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004596 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4597 (intent == null)) {
4598 // All AccountManager error codes are greater than 0
4599 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4600 result.getString(AccountManager.KEY_ERROR_MESSAGE));
4601 } else {
4602 response.onResult(result);
4603 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004604 }
Fred Quintana60307342009-03-24 22:48:12 -07004605 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004606 // if the caller is dead then there is no one to care about remote exceptions
4607 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4608 Log.v(TAG, "failure while notifying response", e);
4609 }
Fred Quintana60307342009-03-24 22:48:12 -07004610 }
4611 }
4612 }
Fred Quintana60307342009-03-24 22:48:12 -07004613
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004614 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004615 public void onRequestContinued() {
4616 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07004617 }
4618
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004619 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004620 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004621 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07004622 IAccountManagerResponse response = getResponseAndClose();
4623 if (response != null) {
4624 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08004625 Log.v(TAG, getClass().getSimpleName()
4626 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07004627 }
4628 try {
4629 response.onError(errorCode, errorMessage);
4630 } catch (RemoteException e) {
4631 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4632 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4633 }
4634 }
4635 } else {
4636 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4637 Log.v(TAG, "Session.onError: already closed");
4638 }
Fred Quintana60307342009-03-24 22:48:12 -07004639 }
4640 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004641
4642 /**
4643 * find the component name for the authenticator and initiate a bind
4644 * if no authenticator or the bind fails then return false, otherwise return true
4645 */
4646 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004647 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4648 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4649 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07004650 if (authenticatorInfo == null) {
4651 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4652 Log.v(TAG, "there is no authenticator for " + authenticatorType
4653 + ", bailing out");
4654 }
4655 return false;
4656 }
4657
Jeff Sharkeyce18c812016-04-27 16:00:41 -06004658 if (!isLocalUnlockedUser(mAccounts.userId)
Jeff Sharkey8a372a02016-03-16 16:25:45 -06004659 && !authenticatorInfo.componentInfo.directBootAware) {
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07004660 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4661 + " which isn't encryption aware");
4662 return false;
4663 }
4664
Fred Quintanab839afc2009-10-14 15:57:28 -07004665 Intent intent = new Intent();
4666 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4667 intent.setComponent(authenticatorInfo.componentName);
4668 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4669 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4670 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08004671 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004672 UserHandle.of(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07004673 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4674 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4675 }
4676 return false;
4677 }
4678
Fred Quintanab839afc2009-10-14 15:57:28 -07004679 return true;
4680 }
Fred Quintana60307342009-03-24 22:48:12 -07004681 }
4682
Svet Ganov5d09c992016-09-07 09:57:41 -07004683 class MessageHandler extends Handler {
Fred Quintana60307342009-03-24 22:48:12 -07004684 MessageHandler(Looper looper) {
4685 super(looper);
4686 }
Costin Manolache3348f142009-09-29 18:58:36 -07004687
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004688 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004689 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07004690 switch (msg.what) {
4691 case MESSAGE_TIMED_OUT:
4692 Session session = (Session)msg.obj;
4693 session.onTimedOut();
4694 break;
4695
Amith Yamasani5be347b2013-03-31 17:44:31 -07004696 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00004697 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07004698 break;
4699
Fred Quintana60307342009-03-24 22:48:12 -07004700 default:
4701 throw new IllegalStateException("unhandled message: " + msg.what);
4702 }
4703 }
4704 }
4705
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004706 private void logRecord(UserAccounts accounts, String action, String tableName) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004707 logRecord(action, tableName, -1, accounts);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004708 }
4709
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004710 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004711 logRecord(action, tableName, -1, accounts, uid);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004712 }
4713
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004714 /*
4715 * This function receives an opened writable database.
4716 */
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004717 private void logRecord(String action, String tableName, long accountId,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004718 UserAccounts userAccount) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004719 logRecord(action, tableName, accountId, userAccount, getCallingUid());
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004720 }
4721
4722 /*
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004723 * This function receives an opened writable database and writes to it in a separate thread.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004724 */
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004725 private void logRecord(String action, String tableName, long accountId,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004726 UserAccounts userAccount, int callingUid) {
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004727
4728 class LogRecordTask implements Runnable {
4729 private final String action;
4730 private final String tableName;
4731 private final long accountId;
4732 private final UserAccounts userAccount;
4733 private final int callingUid;
4734 private final long userDebugDbInsertionPoint;
4735
4736 LogRecordTask(final String action,
4737 final String tableName,
4738 final long accountId,
4739 final UserAccounts userAccount,
4740 final int callingUid,
4741 final long userDebugDbInsertionPoint) {
4742 this.action = action;
4743 this.tableName = tableName;
4744 this.accountId = accountId;
4745 this.userAccount = userAccount;
4746 this.callingUid = callingUid;
4747 this.userDebugDbInsertionPoint = userDebugDbInsertionPoint;
4748 }
4749
4750 public void run() {
4751 SQLiteStatement logStatement = userAccount.statementForLogging;
4752 logStatement.bindLong(1, accountId);
4753 logStatement.bindString(2, action);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004754 logStatement.bindString(3, mDateFormat.format(new Date()));
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004755 logStatement.bindLong(4, callingUid);
4756 logStatement.bindString(5, tableName);
4757 logStatement.bindLong(6, userDebugDbInsertionPoint);
4758 logStatement.execute();
4759 logStatement.clearBindings();
4760 }
4761 }
4762
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004763 LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
4764 callingUid, userAccount.debugDbInsertionPoint);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004765 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004766 % AccountsDb.MAX_DEBUG_DB_SIZE;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004767 mHandler.post(logTask);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004768 }
4769
4770 /*
4771 * This should only be called once to compile the sql statement for logging
4772 * and to find the insertion point.
4773 */
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004774 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(UserAccounts userAccount) {
4775 userAccount.debugDbInsertionPoint = userAccount.accountsDb
4776 .calculateDebugTableInsertionPoint();
4777 userAccount.statementForLogging = userAccount.accountsDb.compileSqlStatementForLogging();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004778 }
4779
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004780 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07004781 return asBinder();
4782 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004783
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004784 /**
4785 * Searches array of arguments for the specified string
4786 * @param args array of argument strings
4787 * @param value value to search for
4788 * @return true if the value is contained in the array
4789 */
4790 private static boolean scanArgs(String[] args, String value) {
4791 if (args != null) {
4792 for (String arg : args) {
4793 if (value.equals(arg)) {
4794 return true;
4795 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004796 }
4797 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004798 return false;
4799 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004800
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004801 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004802 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07004803 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
4804 != PackageManager.PERMISSION_GRANTED) {
4805 fout.println("Permission Denial: can't dump AccountsManager from from pid="
4806 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
4807 + " without permission " + android.Manifest.permission.DUMP);
4808 return;
4809 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004810 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004811 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07004812
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004813 final List<UserInfo> users = getUserManager().getUsers();
4814 for (UserInfo user : users) {
4815 ipw.println("User " + user + ":");
4816 ipw.increaseIndent();
4817 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
4818 ipw.println();
4819 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08004820 }
4821 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004822
Amith Yamasani04e0d262012-02-14 11:50:53 -08004823 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
4824 String[] args, boolean isCheckinRequest) {
4825 synchronized (userAccounts.cacheLock) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004826 if (isCheckinRequest) {
4827 // This is a checkin request. *Only* upload the account types and the count of each.
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004828 userAccounts.accountsDb.dumpDeAccountsTable(fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004829 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004830 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07004831 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004832 fout.println("Accounts: " + accounts.length);
4833 for (Account account : accounts) {
4834 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004835 }
Fred Quintana307da1a2010-01-21 14:24:20 -08004836
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004837 // Add debug information.
4838 fout.println();
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07004839 userAccounts.accountsDb.dumpDebugTable(fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004840 fout.println();
4841 synchronized (mSessions) {
4842 final long now = SystemClock.elapsedRealtime();
4843 fout.println("Active Sessions: " + mSessions.size());
4844 for (Session session : mSessions.values()) {
4845 fout.println(" " + session.toDebugString(now));
4846 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004847 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004848
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004849 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004850 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004851 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004852 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004853 }
4854
Amith Yamasani04e0d262012-02-14 11:50:53 -08004855 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004856 Intent intent, String packageName, final int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004857 long identityToken = clearCallingIdentity();
4858 try {
4859 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4860 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
4861 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004862
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004863 if (intent.getComponent() != null &&
4864 GrantCredentialsPermissionActivity.class.getName().equals(
4865 intent.getComponent().getClassName())) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004866 createNoCredentialsPermissionNotification(account, intent, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004867 } else {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004868 Context contextForUser = getContextForUser(new UserHandle(userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08004869 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07004870 intent.addCategory(String.valueOf(notificationId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004871
Fred Quintana33f889a2009-09-14 17:31:26 -07004872 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004873 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04004874 Notification n = new Notification.Builder(contextForUser)
4875 .setWhen(0)
4876 .setSmallIcon(android.R.drawable.stat_sys_warning)
4877 .setColor(contextForUser.getColor(
4878 com.android.internal.R.color.system_notification_accent_color))
4879 .setContentTitle(String.format(notificationTitleFormat, account.name))
4880 .setContentText(message)
4881 .setContentIntent(PendingIntent.getActivityAsUser(
4882 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004883 null, new UserHandle(userId)))
Chris Wren1ce4b6d2015-06-11 10:19:43 -04004884 .build();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004885 installNotification(notificationId, n, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004886 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004887 } finally {
4888 restoreCallingIdentity(identityToken);
4889 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004890 }
4891
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004892 private void installNotification(int notificationId, final Notification notification,
4893 String packageName, int userId) {
4894 final long token = clearCallingIdentity();
4895 try {
Fyodor Kupolovda993802016-09-21 14:47:10 -07004896 INotificationManager notificationManager = mInjector.getNotificationManager();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004897 try {
4898 notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
4899 notificationId, notification, new int[1], userId);
4900 } catch (RemoteException e) {
4901 /* ignore - local call */
4902 }
4903 } finally {
4904 Binder.restoreCallingIdentity(token);
4905 }
Fred Quintana56285a62010-12-02 14:20:51 -08004906 }
4907
Fyodor Kupolovda993802016-09-21 14:47:10 -07004908 private void cancelNotification(int id, UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004909 cancelNotification(id, mContext.getPackageName(), user);
4910 }
4911
Fyodor Kupolovda993802016-09-21 14:47:10 -07004912 private void cancelNotification(int id, String packageName, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004913 long identityToken = clearCallingIdentity();
4914 try {
Fyodor Kupolovda993802016-09-21 14:47:10 -07004915 INotificationManager service = mInjector.getNotificationManager();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004916 service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
4917 } catch (RemoteException e) {
4918 /* ignore - local call */
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004919 } finally {
4920 restoreCallingIdentity(identityToken);
4921 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004922 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004923
Ian Pedowitz358e51f2016-03-15 17:08:27 +00004924 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
4925 for (String perm : permissions) {
4926 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
4927 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4928 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
4929 }
4930 final int opCode = AppOpsManager.permissionToOpCode(perm);
4931 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
4932 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
4933 return true;
4934 }
4935 }
4936 }
4937 return false;
4938 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004939
Amith Yamasani67df64b2012-12-14 12:09:36 -08004940 private int handleIncomingUser(int userId) {
4941 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08004942 return ActivityManager.getService().handleIncomingUser(
Amith Yamasani67df64b2012-12-14 12:09:36 -08004943 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
4944 } catch (RemoteException re) {
4945 // Shouldn't happen, local.
4946 }
4947 return userId;
4948 }
4949
Christopher Tateccbf84f2013-05-08 15:25:41 -07004950 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004951 final int callingUserId = UserHandle.getUserId(callingUid);
4952
4953 final PackageManager userPackageManager;
4954 try {
4955 userPackageManager = mContext.createPackageContextAsUser(
4956 "android", 0, new UserHandle(callingUserId)).getPackageManager();
4957 } catch (NameNotFoundException e) {
4958 return false;
4959 }
4960
4961 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07004962 for (String name : packages) {
4963 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004964 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08004965 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08004966 && (packageInfo.applicationInfo.privateFlags
4967 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07004968 return true;
4969 }
4970 } catch (PackageManager.NameNotFoundException e) {
4971 return false;
4972 }
4973 }
4974 return false;
4975 }
4976
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004977 private boolean permissionIsGranted(
4978 Account account, String authTokenType, int callerUid, int userId) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004979 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
4980 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4981 Log.v(TAG, "Access to " + account + " granted calling uid is system");
4982 }
4983 return true;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004984 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004985
4986 if (isPrivileged(callerUid)) {
4987 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4988 Log.v(TAG, "Access to " + account + " granted calling uid "
4989 + callerUid + " privileged");
4990 }
4991 return true;
4992 }
4993 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
4994 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4995 Log.v(TAG, "Access to " + account + " granted calling uid "
4996 + callerUid + " manages the account");
4997 }
4998 return true;
4999 }
5000 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5001 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5002 Log.v(TAG, "Access to " + account + " granted calling uid "
5003 + callerUid + " user granted access");
5004 }
5005 return true;
5006 }
5007
5008 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5009 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5010 }
5011
5012 return false;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005013 }
5014
Svetoslavf3f02ac2015-09-08 14:36:35 -07005015 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5016 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005017 if (accountType == null) {
5018 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005019 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07005020 return getTypesVisibleToCaller(callingUid, userId,
5021 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005022 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005023 }
5024
5025 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5026 if (accountType == null) {
5027 return false;
5028 } else {
5029 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5030 }
5031 }
5032
Svetoslavf3f02ac2015-09-08 14:36:35 -07005033 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5034 String opPackageName) {
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005035 boolean isPermitted =
5036 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5037 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005038 return getTypesForCaller(callingUid, userId, isPermitted);
5039 }
5040
5041 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5042 return getTypesForCaller(callingUid, userId, false);
5043 }
5044
5045 private List<String> getTypesForCaller(
5046 int callingUid, int userId, boolean isOtherwisePermitted) {
5047 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005048 long identityToken = Binder.clearCallingIdentity();
5049 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5050 try {
5051 serviceInfos = mAuthenticatorCache.getAllServices(userId);
5052 } finally {
5053 Binder.restoreCallingIdentity(identityToken);
5054 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005055 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005056 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005057 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5058 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5059 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005060 }
5061 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005062 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005063 }
5064
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07005065 private boolean isAccountPresentForCaller(String accountName, String accountType) {
5066 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5067 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5068 if (account.name.equals(accountName)) {
5069 return true;
5070 }
5071 }
5072 }
5073 return false;
5074 }
5075
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07005076 private static void checkManageUsersPermission(String message) {
5077 if (ActivityManager.checkComponentPermission(
5078 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5079 != PackageManager.PERMISSION_GRANTED) {
5080 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5081 }
5082 }
5083
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07005084 private static void checkManageOrCreateUsersPermission(String message) {
5085 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5086 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5087 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5088 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5089 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5090 + message);
5091 }
5092 }
5093
Amith Yamasani04e0d262012-02-14 11:50:53 -08005094 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5095 int callerUid) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005096 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005097 return true;
5098 }
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005099 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005100 synchronized (accounts.cacheLock) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005101 long grantsCount;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005102 if (authTokenType != null) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005103 grantsCount = accounts.accountsDb.findMatchingGrantsCount(callerUid, authTokenType,
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005104 account);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005105 } else {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005106 grantsCount = accounts.accountsDb.findMatchingGrantsCountAnyToken(callerUid,
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005107 account);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005108 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005109 final boolean permissionGranted = grantsCount > 0;
Svet Ganov890a2102016-08-24 00:08:00 -07005110
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005111 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5112 // TODO: Skip this check when running automated tests. Replace this
5113 // with a more general solution.
5114 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08005115 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005116 + " but ignoring since device is in test harness.");
5117 return true;
5118 }
5119 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005120 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005121 }
5122
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005123 private boolean isSystemUid(int callingUid) {
5124 String[] packages = null;
5125 long ident = Binder.clearCallingIdentity();
5126 try {
5127 packages = mPackageManager.getPackagesForUid(callingUid);
5128 } finally {
5129 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005130 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005131 if (packages != null) {
5132 for (String name : packages) {
5133 try {
5134 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5135 if (packageInfo != null
5136 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5137 != 0) {
5138 return true;
5139 }
5140 } catch (PackageManager.NameNotFoundException e) {
5141 Log.w(TAG, String.format("Could not find package [%s]", name), e);
5142 }
5143 }
5144 } else {
5145 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005146 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005147 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00005148 }
5149
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005150 /** Succeeds if any of the specified permissions are granted. */
5151 private void checkReadAccountsPermitted(
5152 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005153 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07005154 int userId,
5155 String opPackageName) {
5156 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005157 String msg = String.format(
5158 "caller uid %s cannot access %s accounts",
5159 callingUid,
5160 accountType);
5161 Log.w(TAG, " " + msg);
5162 throw new SecurityException(msg);
5163 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005164 }
5165
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005166 private boolean canUserModifyAccounts(int userId, int callingUid) {
5167 // the managing app can always modify accounts
5168 if (isProfileOwner(callingUid)) {
5169 return true;
5170 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005171 if (getUserManager().getUserRestrictions(new UserHandle(userId))
5172 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5173 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005174 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005175 return true;
5176 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005177
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005178 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5179 // the managing app can always modify accounts
5180 if (isProfileOwner(callingUid)) {
5181 return true;
5182 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005183 DevicePolicyManager dpm = (DevicePolicyManager) mContext
5184 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005185 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02005186 if (typesArray == null) {
5187 return true;
5188 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005189 for (String forbiddenType : typesArray) {
5190 if (forbiddenType.equals(accountType)) {
5191 return false;
5192 }
5193 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005194 return true;
5195 }
5196
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005197 private boolean isProfileOwner(int uid) {
5198 final DevicePolicyManagerInternal dpmi =
5199 LocalServices.getService(DevicePolicyManagerInternal.class);
5200 return (dpmi != null)
5201 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5202 }
5203
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08005204 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07005205 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5206 throws RemoteException {
5207 final int callingUid = getCallingUid();
5208
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005209 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07005210 throw new SecurityException();
5211 }
5212
5213 if (value) {
5214 grantAppPermission(account, authTokenType, uid);
5215 } else {
5216 revokeAppPermission(account, authTokenType, uid);
5217 }
5218 }
5219
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005220 /**
5221 * Allow callers with the given uid permission to get credentials for account/authTokenType.
5222 * <p>
5223 * Although this is public it can only be accessed via the AccountManagerService object
5224 * which is in the system. This means we don't need to protect it with permissions.
5225 * @hide
5226 */
Svet Ganov5d09c992016-09-07 09:57:41 -07005227 void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005228 if (account == null || authTokenType == null) {
5229 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005230 return;
5231 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005232 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005233 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005234 long accountId = accounts.accountsDb.findDeAccountId(account);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005235 if (accountId >= 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005236 accounts.accountsDb.insertGrant(accountId, authTokenType, uid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005237 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005238 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005239 UserHandle.of(accounts.userId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005240
5241 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005242 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005243
5244 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5245 for (AccountManagerInternal.OnAppPermissionChangeListener listener
5246 : mAppPermissionChangeListeners) {
5247 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5248 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005249 }
5250
5251 /**
5252 * Don't allow callers with the given uid permission to get credentials for
5253 * account/authTokenType.
5254 * <p>
5255 * Although this is public it can only be accessed via the AccountManagerService object
5256 * which is in the system. This means we don't need to protect it with permissions.
5257 * @hide
5258 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005259 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005260 if (account == null || authTokenType == null) {
5261 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005262 return;
5263 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005264 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005265 synchronized (accounts.cacheLock) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005266 accounts.accountsDb.beginTransaction();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005267 try {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005268 long accountId = accounts.accountsDb.findDeAccountId(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005269 if (accountId >= 0) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005270 accounts.accountsDb.deleteGrantsByAccountIdAuthTokenTypeAndUid(
5271 accountId, authTokenType, uid);
5272 accounts.accountsDb.setTransactionSuccessful();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005273 }
5274 } finally {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005275 accounts.accountsDb.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005276 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005277
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005278 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5279 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005280 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005281
5282 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5283 for (AccountManagerInternal.OnAppPermissionChangeListener listener
5284 : mAppPermissionChangeListeners) {
5285 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5286 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005287 }
Fred Quintana56285a62010-12-02 14:20:51 -08005288
Amith Yamasani04e0d262012-02-14 11:50:53 -08005289 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
5290 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005291 if (oldAccountsForType != null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005292 ArrayList<Account> newAccountsList = new ArrayList<>();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005293 for (Account curAccount : oldAccountsForType) {
5294 if (!curAccount.equals(account)) {
5295 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005296 }
5297 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005298 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005299 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005300 } else {
5301 Account[] newAccountsForType = new Account[newAccountsList.size()];
5302 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005303 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005304 }
Fred Quintana56285a62010-12-02 14:20:51 -08005305 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005306 accounts.userDataCache.remove(account);
5307 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005308 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08005309 }
5310
5311 /**
5312 * This assumes that the caller has already checked that the account is not already present.
Svetoslav Ganov57f62592016-09-16 17:29:05 -07005313 * IMPORTANT: The account being inserted will begin to be tracked for access in remote
5314 * processes and if you will return this account to apps you should return the result.
5315 * @return The inserted account which is a new instance that is being tracked.
Fred Quintana56285a62010-12-02 14:20:51 -08005316 */
Svetoslav Ganov57f62592016-09-16 17:29:05 -07005317 private Account insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005318 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005319 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
5320 Account[] newAccountsForType = new Account[oldLength + 1];
5321 if (accountsForType != null) {
5322 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08005323 }
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07005324 String token = account.getAccessId() != null ? account.getAccessId()
5325 : UUID.randomUUID().toString();
5326 newAccountsForType[oldLength] = new Account(account, token);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005327 accounts.accountCache.put(account.type, newAccountsForType);
Svetoslav Ganov57f62592016-09-16 17:29:05 -07005328 return newAccountsForType[oldLength];
Fred Quintana56285a62010-12-02 14:20:51 -08005329 }
5330
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005331 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07005332 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005333 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07005334 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005335 return unfiltered;
5336 }
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07005337 UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
Amith Yamasani0c19bf52013-10-03 10:34:58 -07005338 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005339 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005340 // If any of the packages is a visible listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005341 // otherwise return non-shared accounts only.
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005342 // This might be a temporary way to specify a visible list
5343 String visibleList = mContext.getResources().getString(
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005344 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
5345 for (String packageName : packages) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005346 if (visibleList.contains(";" + packageName + ";")) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005347 return unfiltered;
5348 }
5349 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005350 ArrayList<Account> allowed = new ArrayList<>();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005351 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
5352 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005353 String requiredAccountType = "";
5354 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07005355 // If there's an explicit callingPackage specified, check if that package
5356 // opted in to see restricted accounts.
5357 if (callingPackage != null) {
5358 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005359 if (pi != null && pi.restrictedAccountType != null) {
5360 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07005361 }
5362 } else {
5363 // Otherwise check if the callingUid has a package that has opted in
5364 for (String packageName : packages) {
5365 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
5366 if (pi != null && pi.restrictedAccountType != null) {
5367 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07005368 break;
5369 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005370 }
5371 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005372 } catch (NameNotFoundException nnfe) {
5373 }
5374 for (Account account : unfiltered) {
5375 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005376 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005377 } else {
5378 boolean found = false;
5379 for (Account shared : sharedAccounts) {
5380 if (shared.equals(account)) {
5381 found = true;
5382 break;
5383 }
5384 }
5385 if (!found) {
5386 allowed.add(account);
5387 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005388 }
5389 }
5390 Account[] filtered = new Account[allowed.size()];
5391 allowed.toArray(filtered);
5392 return filtered;
5393 } else {
5394 return unfiltered;
5395 }
5396 }
5397
Amith Yamasani27db4682013-03-30 17:07:47 -07005398 /*
5399 * packageName can be null. If not null, it should be used to filter out restricted accounts
5400 * that the package is not allowed to access.
5401 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005402 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07005403 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005404 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005405 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005406 if (accounts == null) {
5407 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08005408 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005409 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07005410 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08005411 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005412 } else {
5413 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08005414 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005415 totalLength += accounts.length;
5416 }
5417 if (totalLength == 0) {
5418 return EMPTY_ACCOUNT_ARRAY;
5419 }
5420 Account[] accounts = new Account[totalLength];
5421 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08005422 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005423 System.arraycopy(accountsOfType, 0, accounts, totalLength,
5424 accountsOfType.length);
5425 totalLength += accountsOfType.length;
5426 }
Amith Yamasani27db4682013-03-30 17:07:47 -07005427 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08005428 }
5429 }
5430
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005431 protected void writeUserDataIntoCacheLocked(UserAccounts accounts,
Amith Yamasani04e0d262012-02-14 11:50:53 -08005432 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005433 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005434 if (userDataForAccount == null) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005435 userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005436 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005437 }
5438 if (value == null) {
5439 userDataForAccount.remove(key);
5440 } else {
5441 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08005442 }
5443 }
5444
Carlos Valdivia91979be2015-05-22 14:11:35 -07005445 protected String readCachedTokenInternal(
5446 UserAccounts accounts,
5447 Account account,
5448 String tokenType,
5449 String callingPackage,
5450 byte[] pkgSigDigest) {
5451 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005452 return accounts.accountTokenCaches.get(
5453 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07005454 }
5455 }
5456
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005457 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts,
Amith Yamasani04e0d262012-02-14 11:50:53 -08005458 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005459 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005460 if (authTokensForAccount == null) {
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005461 authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005462 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005463 }
5464 if (value == null) {
5465 authTokensForAccount.remove(key);
5466 } else {
5467 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08005468 }
5469 }
5470
Amith Yamasani04e0d262012-02-14 11:50:53 -08005471 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
5472 String authTokenType) {
5473 synchronized (accounts.cacheLock) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005474 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08005475 if (authTokensForAccount == null) {
5476 // need to populate the cache for this account
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005477 authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005478 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005479 }
5480 return authTokensForAccount.get(authTokenType);
5481 }
5482 }
5483
Simranjit Kohli858511c2016-03-10 18:36:11 +00005484 protected String readUserDataInternalLocked(
5485 UserAccounts accounts, Account account, String key) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005486 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00005487 if (userDataForAccount == null) {
5488 // need to populate the cache for this account
Fyodor Kupolov00de49e2016-09-23 13:10:27 -07005489 userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00005490 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005491 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00005492 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08005493 }
5494
Kenny Guy07ad8dc2014-09-01 20:56:12 +01005495 private Context getContextForUser(UserHandle user) {
5496 try {
5497 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
5498 } catch (NameNotFoundException e) {
5499 // Default to mContext, not finding the package system is running as is unlikely.
5500 return mContext;
5501 }
5502 }
Sandra Kwan78812282015-11-04 11:19:47 -08005503
5504 private void sendResponse(IAccountManagerResponse response, Bundle result) {
5505 try {
5506 response.onResult(result);
5507 } catch (RemoteException e) {
5508 // if the caller is dead then there is no one to care about remote
5509 // exceptions
5510 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5511 Log.v(TAG, "failure while notifying response", e);
5512 }
5513 }
5514 }
5515
5516 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
5517 String errorMessage) {
5518 try {
5519 response.onError(errorCode, errorMessage);
5520 } catch (RemoteException e) {
5521 // if the caller is dead then there is no one to care about remote
5522 // exceptions
5523 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5524 Log.v(TAG, "failure while notifying response", e);
5525 }
5526 }
5527 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005528
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005529 private final class AccountManagerInternalImpl extends AccountManagerInternal {
Svet Ganov5d09c992016-09-07 09:57:41 -07005530 private final Object mLock = new Object();
5531
5532 @GuardedBy("mLock")
5533 private AccountManagerBackupHelper mBackupHelper;
5534
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005535 @Override
5536 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
5537 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
5538 if (account == null) {
5539 Slog.w(TAG, "account cannot be null");
5540 return;
5541 }
5542 if (packageName == null) {
5543 Slog.w(TAG, "packageName cannot be null");
5544 return;
5545 }
5546 if (userId < UserHandle.USER_SYSTEM) {
5547 Slog.w(TAG, "user id must be concrete");
5548 return;
5549 }
5550 if (callback == null) {
5551 Slog.w(TAG, "callback cannot be null");
5552 return;
5553 }
5554
Svet Ganovf6d424f12016-09-20 20:18:53 -07005555 if (AccountManagerService.this.hasAccountAccess(account, packageName,
5556 new UserHandle(userId))) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005557 Bundle result = new Bundle();
5558 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
5559 callback.sendResult(result);
5560 return;
5561 }
5562
5563 final int uid;
5564 try {
5565 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
5566 } catch (NameNotFoundException e) {
5567 Slog.e(TAG, "Unknown package " + packageName);
5568 return;
5569 }
5570
5571 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
Svet Ganovf6d424f12016-09-20 20:18:53 -07005572 final UserAccounts userAccounts;
5573 synchronized (mUsers) {
5574 userAccounts = mUsers.get(userId);
5575 }
5576 doNotification(userAccounts, account, null, intent, packageName, userId);
5577 }
5578
5579 @Override
5580 public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) {
5581 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5582 mAppPermissionChangeListeners.add(listener);
5583 }
5584
5585 @Override
5586 public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) {
5587 return AccountManagerService.this.hasAccountAccess(account, null, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005588 }
Svet Ganov5d09c992016-09-07 09:57:41 -07005589
5590 @Override
5591 public byte[] backupAccountAccessPermissions(int userId) {
5592 synchronized (mLock) {
5593 if (mBackupHelper == null) {
5594 mBackupHelper = new AccountManagerBackupHelper(
5595 AccountManagerService.this, this);
5596 }
5597 return mBackupHelper.backupAccountAccessPermissions(userId);
5598 }
5599 }
5600
5601 @Override
5602 public void restoreAccountAccessPermissions(byte[] data, int userId) {
5603 synchronized (mLock) {
5604 if (mBackupHelper == null) {
5605 mBackupHelper = new AccountManagerBackupHelper(
5606 AccountManagerService.this, this);
5607 }
5608 mBackupHelper.restoreAccountAccessPermissions(data, userId);
5609 }
5610 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005611 }
Fyodor Kupolovda993802016-09-21 14:47:10 -07005612
5613 @VisibleForTesting
5614 static class Injector {
5615 private final Context mContext;
5616
5617 public Injector(Context context) {
5618 mContext = context;
5619 }
5620
5621 Looper getMessageHandlerLooper() {
5622 ServiceThread serviceThread = new ServiceThread(TAG,
5623 android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
5624 serviceThread.start();
5625 return serviceThread.getLooper();
5626 }
5627
5628 Context getContext() {
5629 return mContext;
5630 }
5631
5632 void addLocalService(AccountManagerInternal service) {
5633 LocalServices.addService(AccountManagerInternal.class, service);
5634 }
5635
5636 String getDeDatabaseName(int userId) {
5637 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
5638 AccountsDb.DE_DATABASE_NAME);
5639 return databaseFile.getPath();
5640 }
5641
5642 String getCeDatabaseName(int userId) {
5643 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
5644 AccountsDb.CE_DATABASE_NAME);
5645 return databaseFile.getPath();
5646 }
5647
5648 String getPreNDatabaseName(int userId) {
5649 File systemDir = Environment.getDataSystemDirectory();
5650 File databaseFile = new File(Environment.getUserSystemDirectory(userId),
5651 PRE_N_DATABASE_NAME);
5652 if (userId == 0) {
5653 // Migrate old file, if it exists, to the new location.
5654 // Make sure the new file doesn't already exist. A dummy file could have been
5655 // accidentally created in the old location, causing the new one to become corrupted
5656 // as well.
5657 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
5658 if (oldFile.exists() && !databaseFile.exists()) {
5659 // Check for use directory; create if it doesn't exist, else renameTo will fail
5660 File userDir = Environment.getUserSystemDirectory(userId);
5661 if (!userDir.exists()) {
5662 if (!userDir.mkdirs()) {
5663 throw new IllegalStateException("User dir cannot be created: " + userDir);
5664 }
5665 }
5666 if (!oldFile.renameTo(databaseFile)) {
5667 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
5668 }
5669 }
5670 }
5671 return databaseFile.getPath();
5672 }
5673
5674 IAccountAuthenticatorCache getAccountAuthenticatorCache() {
5675 return new AccountAuthenticatorCache(mContext);
5676 }
5677
5678 INotificationManager getNotificationManager() {
5679 return NotificationManager.getService();
5680 }
5681 }
Fred Quintana60307342009-03-24 22:48:12 -07005682}