blob: 575018d6b908ff3c2c1b2f0e00a7309b6d0b107d [file] [log] [blame]
Fred Quintana60307342009-03-24 22:48:12 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.accounts;
Fred Quintana60307342009-03-24 22:48:12 -070018
Doug Zongker885cfc232009-10-21 16:52:44 -070019import android.Manifest;
Carlos Valdivia91979be2015-05-22 14:11:35 -070020import android.accounts.AbstractAccountAuthenticator;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080021import android.accounts.Account;
22import android.accounts.AccountAndUser;
23import android.accounts.AccountAuthenticatorResponse;
24import android.accounts.AccountManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070025import android.accounts.AccountManagerInternal;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080026import android.accounts.AuthenticatorDescription;
Amith Yamasani23c8b962013-04-10 13:37:18 -070027import android.accounts.CantAddAccountActivity;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080028import android.accounts.GrantCredentialsPermissionActivity;
29import android.accounts.IAccountAuthenticator;
30import android.accounts.IAccountAuthenticatorResponse;
31import android.accounts.IAccountManager;
32import android.accounts.IAccountManagerResponse;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070033import android.annotation.IntRange;
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -070034import android.annotation.NonNull;
Svet Ganovf6d424f12016-09-20 20:18:53 -070035import android.annotation.Nullable;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080036import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070037import android.app.ActivityManagerNative;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070038import android.app.ActivityThread;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070039import android.app.AppGlobals;
Svetoslavf3f02ac2015-09-08 14:36:35 -070040import android.app.AppOpsManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070041import android.app.INotificationManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070042import android.app.Notification;
43import android.app.NotificationManager;
44import android.app.PendingIntent;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000045import android.app.admin.DeviceAdminInfo;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010046import android.app.admin.DevicePolicyManager;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000047import android.app.admin.DevicePolicyManagerInternal;
Fred Quintanaa698f422009-04-08 19:14:54 -070048import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070049import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070050import android.content.Context;
51import android.content.Intent;
52import android.content.IntentFilter;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070053import android.content.IntentSender;
Fred Quintanab839afc2009-10-14 15:57:28 -070054import android.content.ServiceConnection;
Carlos Valdivia6ede9c32016-03-10 20:12:32 -080055import android.content.pm.ActivityInfo;
Doug Zongker885cfc232009-10-21 16:52:44 -070056import android.content.pm.ApplicationInfo;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070057import android.content.pm.IPackageManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070058import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070059import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070060import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070061import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070062import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070063import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070064import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070065import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070066import android.database.Cursor;
Fred Quintanaa698f422009-04-08 19:14:54 -070067import android.database.sqlite.SQLiteDatabase;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070068import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070069import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070070import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080071import android.os.Environment;
Fred Quintanaa698f422009-04-08 19:14:54 -070072import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070073import android.os.IBinder;
74import android.os.Looper;
75import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070076import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070077import android.os.Process;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070078import android.os.RemoteCallback;
Fred Quintanaa698f422009-04-08 19:14:54 -070079import android.os.RemoteException;
80import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070081import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070082import android.os.UserManager;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070083import android.os.storage.StorageManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070084import android.text.TextUtils;
85import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070086import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070087import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080088import android.util.SparseArray;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070089import android.util.SparseBooleanArray;
Fred Quintana60307342009-03-24 22:48:12 -070090
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070091import com.android.internal.R;
Svet Ganov5d09c992016-09-07 09:57:41 -070092import com.android.internal.annotations.GuardedBy;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -070093import com.android.internal.annotations.VisibleForTesting;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070094import com.android.internal.content.PackageMonitor;
Amith Yamasani67df64b2012-12-14 12:09:36 -080095import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -080096import com.android.internal.util.IndentingPrintWriter;
Fyodor Kupolov35f68082016-04-06 12:14:17 -070097import com.android.internal.util.Preconditions;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000098import com.android.server.LocalServices;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -070099import com.android.server.ServiceThread;
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600100import com.android.server.SystemService;
Fyodor Kupolov1ce01612016-08-26 11:39:07 -0700101import com.android.server.accounts.AccountsDb.CeDatabaseHelper;
102import com.android.server.accounts.AccountsDb.DeDatabaseHelper;
103import com.android.server.accounts.AccountsDb.DebugDbHelper;
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600104
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700105import com.google.android.collect.Lists;
106import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700107
Oscar Montemayora8529f62009-11-18 10:14:20 -0800108import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -0700109import java.io.FileDescriptor;
110import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -0800111import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -0700112import java.security.MessageDigest;
113import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700114import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -0700115import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -0800116import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700117import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700118import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700119import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700120import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -0800121import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700122import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800123import java.util.Map;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800124import java.util.Map.Entry;
Svet Ganovc1c0d1c2016-09-23 19:15:47 -0700125import java.util.Objects;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700126import java.util.Set;
Svet Ganovc1c0d1c2016-09-23 19:15:47 -0700127import java.util.UUID;
Svet Ganovf6d424f12016-09-20 20:18:53 -0700128import java.util.concurrent.CopyOnWriteArrayList;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700129import java.util.concurrent.atomic.AtomicInteger;
130import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700131
Fred Quintana60307342009-03-24 22:48:12 -0700132/**
133 * A system service that provides account, password, and authtoken management for all
134 * accounts on the device. Some of these calls are implemented with the help of the corresponding
135 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
136 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700137 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700138 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700139 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700140public class AccountManagerService
141 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800142 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -0700143 private static final String TAG = "AccountManagerService";
144
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600145 public static class Lifecycle extends SystemService {
146 private AccountManagerService mService;
147
148 public Lifecycle(Context context) {
149 super(context);
150 }
151
152 @Override
153 public void onStart() {
Fyodor Kupolovda993802016-09-21 14:47:10 -0700154 mService = new AccountManagerService(new Injector(getContext()));
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600155 publishBinderService(Context.ACCOUNT_SERVICE, mService);
156 }
157
158 @Override
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600159 public void onUnlockUser(int userHandle) {
160 mService.onUnlockUser(userHandle);
161 }
162 }
163
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700164 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700165
Svet Ganov5d09c992016-09-07 09:57:41 -0700166 final Context mContext;
Fred Quintana60307342009-03-24 22:48:12 -0700167
Fred Quintana56285a62010-12-02 14:20:51 -0800168 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700169 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700170 private UserManager mUserManager;
Fyodor Kupolovda993802016-09-21 14:47:10 -0700171 private final Injector mInjector;
Fred Quintana56285a62010-12-02 14:20:51 -0800172
Svet Ganov5d09c992016-09-07 09:57:41 -0700173 final MessageHandler mHandler;
Tejas Khorana7b88f0e2016-06-13 13:06:35 -0700174
Fred Quintana60307342009-03-24 22:48:12 -0700175 // Messages that can be sent on mHandler
176 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700177 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700178
Fred Quintana56285a62010-12-02 14:20:51 -0800179 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700180 private static final String PRE_N_DATABASE_NAME = "accounts.db";
Fred Quintana7be59642009-08-24 18:29:25 -0700181 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800182
Carlos Valdivia91979be2015-05-22 14:11:35 -0700183 static {
184 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
185 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
186 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700187
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800188
Fred Quintanaa698f422009-04-08 19:14:54 -0700189 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700190 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
191
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700192 private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
193
Amith Yamasani04e0d262012-02-14 11:50:53 -0800194 static class UserAccounts {
195 private final int userId;
Svet Ganov5d09c992016-09-07 09:57:41 -0700196 final DeDatabaseHelper openHelper;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800197 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
198 credentialsPermissionNotificationIds =
199 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
200 private final HashMap<Account, Integer> signinRequiredNotificationIds =
201 new HashMap<Account, Integer>();
Svet Ganov5d09c992016-09-07 09:57:41 -0700202 final Object cacheLock = new Object();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800203 /** protected by the {@link #cacheLock} */
Svet Ganov5d09c992016-09-07 09:57:41 -0700204 final HashMap<String, Account[]> accountCache =
Svet Ganovf6d424f12016-09-20 20:18:53 -0700205 new LinkedHashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800206 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700207 private final Map<Account, Map<String, String>> userDataCache = new HashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800208 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700209 private final Map<Account, Map<String, String>> authTokenCache = new HashMap<>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700210
211 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700212 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700213
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700214 /** protected by the {@link #cacheLock} */
215 private final Map<String, ArrayList<Integer>> mApplicationAccountRequestMappings =
216 new HashMap<>();
217
218 /* Together the below two Sparse Arrays serve as visible list. One maps UID to account
219 number. Another maps Account number to Account.*/
220
221 /** protected by the {@link #cacheLock} */
222 private final SparseArray<ArrayList<Integer>> mVisibleListUidToMockAccountNumbers =
223 new SparseArray<>();
224
225 //TODO: Instead of using Mock Account IDs, use the actual account IDs.
226 /** protected by the {@link #cacheLock} */
227 private final SparseArray<Account> mMockAccountIdToAccount = new SparseArray<>();
228
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700229 /**
230 * protected by the {@link #cacheLock}
231 *
232 * Caches the previous names associated with an account. Previous names
233 * should be cached because we expect that when an Account is renamed,
234 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
235 * want to know if the accounts they care about have been renamed.
236 *
237 * The previous names are wrapped in an {@link AtomicReference} so that
238 * we can distinguish between those accounts with no previous names and
239 * those whose previous names haven't been cached (yet).
240 */
241 private final HashMap<Account, AtomicReference<String>> previousNameCache =
242 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800243
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700244 private int debugDbInsertionPoint = -1;
245 private SQLiteStatement statementForLogging;
246
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700247 UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800248 this.userId = userId;
249 synchronized (cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700250 openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800251 }
252 }
253 }
254
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700255 private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600256 private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -0700257 // Not thread-safe. Only use in synchronized context
258 private final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Svet Ganovf6d424f12016-09-20 20:18:53 -0700259 private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
260 mAppPermissionChangeListeners = new CopyOnWriteArrayList<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800261
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700262 private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
Fred Quintana31957f12009-10-21 13:43:10 -0700263 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700264
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700265 /**
266 * This should only be called by system code. One should only call this after the service
267 * has started.
268 * @return a reference to the AccountManagerService instance
269 * @hide
270 */
271 public static AccountManagerService getSingleton() {
272 return sThis.get();
273 }
Fred Quintana60307342009-03-24 22:48:12 -0700274
Fyodor Kupolovda993802016-09-21 14:47:10 -0700275 public AccountManagerService(Injector injector) {
276 mInjector = injector;
277 mContext = injector.getContext();
278 mPackageManager = mContext.getPackageManager();
Svetoslavf3f02ac2015-09-08 14:36:35 -0700279 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fyodor Kupolovda993802016-09-21 14:47:10 -0700280 mHandler = new MessageHandler(injector.getMessageHandlerLooper());
281 mAuthenticatorCache = mInjector.getAccountAuthenticatorCache();
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800282 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700283
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700284 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800285
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700286 addRequestsForPreInstalledApplications();
287
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800288 IntentFilter intentFilter = new IntentFilter();
289 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
290 intentFilter.addDataScheme("package");
291 mContext.registerReceiver(new BroadcastReceiver() {
292 @Override
293 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700294 // Don't delete accounts when updating a authenticator's
295 // package.
296 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700297 /* Purging data requires file io, don't block the main thread. This is probably
298 * less than ideal because we are introducing a race condition where old grants
299 * could be exercised until they are purged. But that race condition existed
300 * anyway with the broadcast receiver.
301 *
302 * Ideally, we would completely clear the cache, purge data from the database,
303 * and then rebuild the cache. All under the cache lock. But that change is too
304 * large at this point.
305 */
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700306 Runnable purgingRunnable = new Runnable() {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700307 @Override
308 public void run() {
309 purgeOldGrantsAll();
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700310
311 /* clears application request's for account types supported */
312 int uidOfUninstalledApplication =
313 intent.getIntExtra(Intent.EXTRA_UID, -1);
314 if(uidOfUninstalledApplication != -1) {
315 clearRequestedAccountVisibility(uidOfUninstalledApplication,
316 getUserAccounts(UserHandle.getUserId(
317 uidOfUninstalledApplication)));
318 }
319
320 /* removes visibility of previous UID of this uninstalled application*/
321 removeAccountVisibilityAllAccounts(uidOfUninstalledApplication,
322 getUserAccounts(UserHandle.getUserId(
323 uidOfUninstalledApplication)));
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700324 }
325 };
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700326 mHandler.post(purgingRunnable);
Carlos Valdivia23f58262014-09-05 10:52:41 -0700327 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700328
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800329 }
330 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800331
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700332 IntentFilter packageAddedOrChangedFilter = new IntentFilter();
333 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
334 packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
335 packageAddedOrChangedFilter.addDataScheme("package");
336 mContext.registerReceiverAsUser(new BroadcastReceiver() {
337 @Override
338 public void onReceive(Context context1, Intent intent) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700339 mHandler.post(new Runnable() {
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700340 @Override
341 public void run() {
342 int uidOfInstalledApplication =
343 intent.getIntExtra(Intent.EXTRA_UID, -1);
344 if(uidOfInstalledApplication != -1) {
345 registerAccountTypesSupported(
346 uidOfInstalledApplication,
347 getUserAccounts(
348 UserHandle.getUserId(uidOfInstalledApplication)));
349 }
350 }
351 });
352 }
353 }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
354
Amith Yamasani13593602012-03-22 16:16:17 -0700355 IntentFilter userFilter = new IntentFilter();
356 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800357 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700358 @Override
359 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800360 String action = intent.getAction();
361 if (Intent.ACTION_USER_REMOVED.equals(action)) {
362 onUserRemoved(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800363 }
Amith Yamasani13593602012-03-22 16:16:17 -0700364 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800365 }, UserHandle.ALL, userFilter, null, null);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700366
Fyodor Kupolovda993802016-09-21 14:47:10 -0700367 injector.addLocalService(new AccountManagerInternalImpl());
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700368
369 // Need to cancel account request notifications if the update/install can access the account
370 new PackageMonitor() {
371 @Override
372 public void onPackageAdded(String packageName, int uid) {
373 // Called on a handler, and running as the system
374 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
375 }
376
377 @Override
378 public void onPackageUpdateFinished(String packageName, int uid) {
379 // Called on a handler, and running as the system
380 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
381 }
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700382 }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700383
384 // Cancel account request notification if an app op was preventing the account access
385 mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
386 new AppOpsManager.OnOpChangedInternalListener() {
387 @Override
388 public void onOpChanged(int op, String packageName) {
389 try {
390 final int userId = ActivityManager.getCurrentUser();
391 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
392 final int mode = mAppOpsManager.checkOpNoThrow(
393 AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
394 if (mode == AppOpsManager.MODE_ALLOWED) {
395 final long identity = Binder.clearCallingIdentity();
396 try {
397 cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
398 } finally {
399 Binder.restoreCallingIdentity(identity);
400 }
401 }
402 } catch (NameNotFoundException e) {
403 /* ignore */
404 }
405 }
406 });
407
408 // Cancel account request notification if a permission was preventing the account access
409 mPackageManager.addOnPermissionsChangeListener(
410 (int uid) -> {
411 Account[] accounts = null;
412 String[] packageNames = mPackageManager.getPackagesForUid(uid);
413 if (packageNames != null) {
414 final int userId = UserHandle.getUserId(uid);
415 final long identity = Binder.clearCallingIdentity();
416 try {
417 for (String packageName : packageNames) {
Fyodor Kupolovda993802016-09-21 14:47:10 -0700418 if (mPackageManager.checkPermission(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700419 Manifest.permission.GET_ACCOUNTS, packageName)
420 != PackageManager.PERMISSION_GRANTED) {
421 continue;
422 }
423
424 if (accounts == null) {
425 accounts = getAccountsAsUser(null, userId, "android");
426 if (ArrayUtils.isEmpty(accounts)) {
427 return;
428 }
429 }
430
431 for (Account account : accounts) {
432 cancelAccountAccessRequestNotificationIfNeeded(
433 account, uid, packageName, true);
434 }
435 }
436 } finally {
437 Binder.restoreCallingIdentity(identity);
438 }
439 }
440 });
441 }
442
443 private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
444 boolean checkAccess) {
445 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
446 for (Account account : accounts) {
447 cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
448 }
449 }
450
451 private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
452 boolean checkAccess) {
453 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
454 for (Account account : accounts) {
455 cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
456 }
457 }
458
459 private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
460 boolean checkAccess) {
461 String[] packageNames = mPackageManager.getPackagesForUid(uid);
462 if (packageNames != null) {
463 for (String packageName : packageNames) {
464 cancelAccountAccessRequestNotificationIfNeeded(account, uid,
465 packageName, checkAccess);
466 }
467 }
468 }
469
470 private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
471 int uid, String packageName, boolean checkAccess) {
472 if (!checkAccess || hasAccountAccess(account, packageName,
473 UserHandle.getUserHandleForUid(uid))) {
474 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -0700475 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700476 UserHandle.getUserHandleForUid(uid));
477 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800478 }
479
Dianne Hackborn164371f2013-10-01 19:10:13 -0700480 @Override
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700481 public boolean addAccountExplicitlyWithUid(Account account, String password, Bundle extras,
482 int[] selectedUids) {
483 if(addAccountExplicitly(account,password,extras)) {
484 for(int thisUid : selectedUids) {
485 makeAccountVisible(account, thisUid);
486 }
487 return true;
488 }
489 return false;
490 }
491
492 @Override
493 public int[] getRequestingUidsForType(String accountType) {
494 int callingUid = Binder.getCallingUid();
495 if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
496 String msg = String.format(
497 "uid %s cannot get secrets for accounts of type: %s",
498 callingUid,
499 accountType);
500 throw new SecurityException(msg);
501 }
502 return getRequestingUidsForType(accountType, getUserAccounts(
503 UserHandle.getUserId(callingUid)));
504 }
505
506 /**
507 * Returns all UIDs for applications that requested the account type. This method
508 * is called indirectly by the Authenticator and AccountManager
509 *
510 * @param accountType authenticator would like to know the requesting apps of
511 * @param ua UserAccount that currently hosts the account and application
512 *
513 * @return ArrayList of all UIDs that support accounts of this
514 * account type that seek approval (to be used to know which accounts for
515 * the authenticator to include in addAccountExplicitly). Null if none.
516 */
517 private int[] getRequestingUidsForType(String accountType, UserAccounts ua) {
518 synchronized(ua.cacheLock) {
519 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
520 ua.mApplicationAccountRequestMappings;
521 ArrayList<Integer> allUidsForAccountType = userApplicationAccountRequestMappings.get(
522 accountType);
523 if(allUidsForAccountType == null) {
524 return null;
525 }
526 int[] toReturn = new int[allUidsForAccountType.size()];
527 for(int i = 0 ; i < toReturn.length ; i++) {
528 toReturn[i] = allUidsForAccountType.get(i);
529 }
530 return toReturn;
531 }
532 }
533
534 @Override
535 public boolean isAccountVisible(Account a, int uid) {
536 int callingUid = Binder.getCallingUid();
537 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
538 String msg = String.format(
539 "uid %s cannot get secrets for accounts of type: %s",
540 callingUid,
541 a.type);
542 throw new SecurityException(msg);
543 }
544 return isAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
545 }
546
547 /**
548 * Checks visibility of certain account of a process identified
549 * by a given UID. This is called by the Authenticator indirectly.
550 *
551 * @param a The account to check visibility of
552 * @param uid UID to check visibility of
553 * @param ua UserAccount that currently hosts the account and application
554 *
555 * @return True if application has access to the account
556 *
557 */
558 private boolean isAccountVisible(Account a, int uid, UserAccounts ua) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700559 if(isAccountManagedByCaller(a.type, uid, UserHandle.getUserId(uid))) {
560 return true;
561 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700562 int accountMapping = getMockAccountNumber(a, ua);
563 if(accountMapping < 0) {
564 return true;
565 }
566 synchronized(ua.cacheLock) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700567 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700568 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
569 ua.mVisibleListUidToMockAccountNumbers;
570 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
Tejas Khorana69990d92016-08-03 11:19:40 -0700571 int indexOfAccountMapping = userAcctIdToAcctMap.indexOfValueByValue(a);
572 return indexOfAccountMapping == -1 || (linkedAccountsToUid != null
573 && linkedAccountsToUid.contains(accountMapping));
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700574 }
575 }
576
577 @Override
578 public boolean makeAccountVisible(Account a, int uid) {
579 int callingUid = Binder.getCallingUid();
580 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
581 String msg = String.format(
582 "uid %s cannot get secrets for accounts of type: %s",
583 callingUid,
584 a.type);
585 throw new SecurityException(msg);
586 }
587 return makeAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
588 }
589
590 /**
591 * Gives a certain UID, represented a application, access to an account. This method
592 * is called indirectly by the Authenticator.
593 *
594 * @param a Account to make visible
595 * @param uid to add visibility of the Account from
596 * @param ua UserAccount that currently hosts the account and application
597 *
598 * @return True if account made visible to application and was not previously visible.
599 */
600 private boolean makeAccountVisible(Account a, int uid, UserAccounts ua) {
601 int accountMapping = getMockAccountNumber(a, ua);
602 if(accountMapping < 0) {
603 accountMapping = makeAccountNumber(a, ua);
604 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700605 synchronized(ua.cacheLock) {
606 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
607 ua.mVisibleListUidToMockAccountNumbers;
608 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
609 if(linkedAccountsToUid == null) {
610 linkedAccountsToUid = new ArrayList<>();
611 linkedAccountsToUid.add(accountMapping);
612 userWlUidToMockAccountNums.put(uid, linkedAccountsToUid);
613 } else if(!linkedAccountsToUid.contains(accountMapping)) {
614 linkedAccountsToUid.add(accountMapping);
615 } else {
616 return false;
617 }
618 }
619
620 String[] subPackages = mPackageManager.getPackagesForUid(uid);
621 if(subPackages != null) {
622 for(String subPackage : subPackages) {
623 sendNotification(subPackage, a);
624 }
625 }
626 return true;
627 }
628
629 @Override
630 public boolean removeAccountVisibility(Account a, int uid) {
631 int callingUid = Binder.getCallingUid();
632 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
633 String msg = String.format(
634 "uid %s cannot get secrets for accounts of type: %s",
635 callingUid,
636 a.type);
637 throw new SecurityException(msg);
638 }
639 return removeAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
640 }
641
642 /**
643 * Removes visibility of certain account of a process identified
644 * by a given UID to an application. This is called directly by the
645 * AccountManager and indirectly by the Authenticator.
646 *
647 * @param a Account to remove visibility from
648 * @param uid UID to remove visibility of the Account from
649 * @param ua UserAccount that hosts the account and application
650 *
651 * @return True if application access to account removed and was previously visible.
652 */
653 private boolean removeAccountVisibility(Account a, int uid, UserAccounts ua) {
654 int accountMapping = getMockAccountNumber(a, ua);
655 if(accountMapping < 0) {
656 return false;
657 }
658 synchronized(ua.cacheLock) {
659 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
660 ua.mVisibleListUidToMockAccountNumbers;
661 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
662 if(linkedAccountsToUid != null) {
663 boolean toReturn = linkedAccountsToUid.remove((Integer) accountMapping);
664 if(linkedAccountsToUid.size() == 0) {
665 userWlUidToMockAccountNums.remove(uid);
666 }
667 return toReturn;
668 }
669 }
670 return false;
671 }
672
673 /**
674 * Registers an application's preferences for supported account types for login. This is
675 * a helper method of requestAccountVisibility and indirectly called by AccountManager.
676 *
677 * @param accountTypes account types third party app is willing to support
678 * @param uid of application requesting account visibility
679 * @param ua UserAccount that hosts the account and application
680 */
681 private void addRequestedAccountsVisibility(String[] accountTypes, int uid, UserAccounts ua) {
682 synchronized(ua.cacheLock) {
683 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
684 ua.mApplicationAccountRequestMappings;
685 for(String accountType : accountTypes) {
686 ArrayList<Integer> accountUidAppList = userApplicationAccountRequestMappings
687 .get(accountType);
688 if(accountUidAppList == null) {
689 accountUidAppList = new ArrayList<>();
690 accountUidAppList.add(uid);
691 userApplicationAccountRequestMappings.put(accountType, accountUidAppList);
692 } else if (!accountUidAppList.contains(uid)) {
693 accountUidAppList.add(uid);
694 }
695 }
696 }
697 }
698
699 /**
700 * Registers the requested login account types requested by all the applications already
701 * installed on the device.
702 */
703 private void addRequestsForPreInstalledApplications() {
Fyodor Kupolovda993802016-09-21 14:47:10 -0700704 List<PackageInfo> allInstalledPackages = mPackageManager.getInstalledPackages(0);
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700705 for(PackageInfo pi : allInstalledPackages) {
706 int currentUid = pi.applicationInfo.uid;
707 if(currentUid != -1) {
708 registerAccountTypesSupported(currentUid,
709 getUserAccounts(UserHandle.getUserId(currentUid)));
710 }
711 }
712 }
713
714 /**
715 * Clears all preferences an application had for login account types it offered
716 * support for. This method is used by AccountManager after application is
717 * uninstalled.
718 *
719 * @param uid Uid of the application to clear account type preferences
720 * @param ua UserAccount that hosted the account and application
721 *
722 * @return true if any previous settings were overridden.
723 */
724 private boolean clearRequestedAccountVisibility(int uid, UserAccounts ua) {
725 boolean accountsDeleted = false;
726 ArrayList<String> accountTypesToRemove = new ArrayList<>();
727 synchronized(ua.cacheLock) {
728 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
729 ua.mApplicationAccountRequestMappings;
730 Set<Entry<String, ArrayList<Integer>>> accountTypeAppListEntries =
731 userApplicationAccountRequestMappings.entrySet();
732
733 for(Entry<String, ArrayList<Integer>> entry : accountTypeAppListEntries) {
734 ArrayList<Integer> supportedApps = entry.getValue();
735 if(supportedApps.remove((Integer) uid)) {
736 accountsDeleted = true;
737 }
738
739 if(supportedApps.isEmpty()) {
740 accountTypesToRemove.add(entry.getKey());
741 }
742 }
743
744 for(String s : accountTypesToRemove) {
745 userApplicationAccountRequestMappings.remove(s);
746 }
747 }
748
749 return accountsDeleted;
750 }
751
752 /**
753 * Retrieves the mock account number associated with an Account in order to later retrieve
754 * the account from the Integer-Account Mapping. An account number is not the same as
755 * accountId in the database. This method can be indirectly called by AccountManager and
756 * indirectly by the Authenticator.
757 *
758 * @param a account to retrieve account number mapping
759 * @param ua UserAccount that currently hosts the account and application
760 *
761 * @return account number affiliated with the Account in question. Negative number if none.
762 */
763 private int getMockAccountNumber(Account a, UserAccounts ua) {
764 //TODO: Each account is linked to AccountId rather than generated mock account numbers
765 SparseArray<Account> userAcctIdToAcctMap =
766 ua.mMockAccountIdToAccount;
767 synchronized(ua.cacheLock) {
768 int indexOfAccount = userAcctIdToAcctMap.indexOfValueByValue(a);
769 if(indexOfAccount < 0) {
770 return -1;
771 }
772 return userAcctIdToAcctMap.keyAt(indexOfAccount);
773 }
774 }
775
776 /**
777 * Returns a full list of accounts that a certain UID is allowed access
778 * based on the visible list entries.
779 *
780 * @param uid of application to retrieve visible listed accounts for
781 * @param ua UserAccount that currently hosts the account and application
782 *
783 * @return array of Account values that are accessible by the given uids
784 */
785 private Account[] getVisibleListedAccounts(int uid, UserAccounts ua) {
786 ArrayList<Account> visibleListedAccounts = new ArrayList<>();
787 synchronized(ua.cacheLock) {
788 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
789 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
790 ua.mVisibleListUidToMockAccountNumbers;
791 ArrayList<Integer> visibleListedUidAccountNumbers =
792 userWlUidToMockAccountNums.get(uid);
793 if(visibleListedUidAccountNumbers != null) {
794 for(Integer accountNumber : visibleListedUidAccountNumbers) {
795 Account currentAccount = userAcctIdToAcctMap.get(accountNumber);
796 visibleListedAccounts.add(currentAccount);
797 }
798 }
799 }
800 Account[] arrVisibleListedAccounts = new Account[visibleListedAccounts.size()];
801 return visibleListedAccounts.toArray(arrVisibleListedAccounts);
802 }
803
804 /**
805 * Makes an account number for a given Account to be mapped to.
806 * This method is called by makeVisible if an Account does not have
807 * a mapping for the visible list. This method is thus indirectly
808 * called by the Authenticator.
809 *
810 * @param a account to make an account number mapping of
811 * @param ua UserAccount that currently hosts the account and application
812 *
813 * @return account number created to map to the given account
814 */
815 // TODO: Remove this method and use accountId from DB.
816 private int makeAccountNumber(Account a, UserAccounts ua) {
817 synchronized(ua.cacheLock) {
818 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
819 int newAccountMapping = 0;
820 while(userAcctIdToAcctMap.get(newAccountMapping) != null) {
821 newAccountMapping++;
822 }
823 userAcctIdToAcctMap.put(newAccountMapping, a);
824 return newAccountMapping;
825 }
826 }
827
828
829
830 /**
831 * Registers an application, represented by a UID, to support account types detailed in
832 * the applications manifest as well as allowing it to opt for notifications.
833 *
834 * @param uid UID of application
835 * @param ua UserAccount that currently hosts the account and application
836 */
837 private void registerAccountTypesSupported(int uid, UserAccounts ua) {
838 /* Account types supported are drawn from the Android Manifest of the Application */
839 String interestedPackages = null;
840 try {
841 String[] allPackages = mPackageManager.getPackagesForUid(uid);
Nicolas Prevotf7d8df12016-09-16 17:45:34 +0100842 if (allPackages != null) {
843 for(String aPackage : allPackages) {
844 ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
845 PackageManager.GET_META_DATA);
846 Bundle b = ai.metaData;
847 if(b == null) {
848 return;
849 }
850 interestedPackages = b.getString("android.accounts.SupportedLoginTypes");
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700851 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700852 }
853 } catch (PackageManager.NameNotFoundException e) {
854 Log.d("NameNotFoundException", e.getMessage());
855 }
856 if(interestedPackages != null) {
857 /* request remote account types directly from here. Reads from Android Manifest */
858 requestAccountVisibility(interestedPackages.split(";"), uid, ua);
859 }
860 }
861
862 /**
863 * Allows AccountManager to register account types that an application has login
864 * support for. This method over-writes all of the application's previous settings
865 * for accounts it supported.
866 *
867 * @param accountTypes array of account types application wishes to support
868 * @param uid of application registering requested account types
869 * @param ua UserAccount that hosts the account and application
870 */
871 private void requestAccountVisibility(String[] accountTypes, int uid, UserAccounts ua) {
872 if(accountTypes.length > 0) {
873 clearRequestedAccountVisibility(uid, ua);
874 addRequestedAccountsVisibility(accountTypes, uid, ua);
875 }
876 }
877
878 /**
879 * Removes visibility of all Accounts to this particular UID. This is called when an
880 * application is uninstalled so another application that is installed with the same
881 * UID cannot access Accounts. This is called by AccountManager.
882 *
883 * @param uid of application to remove all Account visibility to
884 * @param ua UserAccount that hosts the current Account
885 */
886 private void removeAccountVisibilityAllAccounts(int uid, UserAccounts ua) {
887 synchronized(ua.cacheLock) {
888 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
889 ua.mVisibleListUidToMockAccountNumbers;
890 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
891 ArrayList<Integer> allAccountNumbersList = userWlUidToMockAccountNums.get(uid);
892 if(allAccountNumbersList != null) {
893 Integer[] allAccountNumbers = allAccountNumbersList.toArray(
894 new Integer[allAccountNumbersList.size()]);
895 for(int accountNum : allAccountNumbers) {
896 removeAccountVisibility(userAcctIdToAcctMap.get(accountNum), uid, ua);
897 }
898 }
899 }
900 }
901
902 /**
903 * Removes visible list functionality of a certain Account.
904 * This method is currently called by (1) addAccountExplicitly (as opposed to
905 * addAccountExplicitlyWithUid) and (2) removeAccountExplicitly.
906 *
907 * @param a the account to clear the visible list functionality for
908 * @param ua currently UserAccounts profile containing Account
909 *
910 * @return true if account previously had visible list functionality
911 */
912 private boolean removeVisibleListFunctionality(Account a, UserAccounts ua) {
913 int mockAccountNum = getMockAccountNumber(a, ua);
914 if(mockAccountNum < 0) {
915 return false;
916 }
917 synchronized(ua.cacheLock) {
918 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
919 ua.mVisibleListUidToMockAccountNumbers;
920 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
921
922 /* Removing mapping from account number to account removes visible list functionality*/
923 userAcctIdToAcctMap.remove(mockAccountNum);
924
925 for(int i = userWlUidToMockAccountNums.size() - 1 ; i >= 0 ; i--) {
926 int uidKey = userWlUidToMockAccountNums.keyAt(i);
927 ArrayList<Integer> allAccountNumbers = userWlUidToMockAccountNums.get(uidKey);
928 if(allAccountNumbers != null) {
929 allAccountNumbers.remove(mockAccountNum);
930 if(allAccountNumbers.isEmpty()) {
931 userWlUidToMockAccountNums.remove(uidKey);
932 }
933 }
934 }
935 }
936 return true;
937 }
938
939 /**
940 * Sends a direct intent to a package, notifying it of a visible account. This
941 * method is a helper method of makeAccountVisible.
942 *
943 * @param desiredPackage to send Account to
944 * @param visibleAccount to send to package
945 */
946 private void sendNotification(String desiredPackage, Account visibleAccount) {
947 Intent intent = new Intent();
948 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
949 intent.setAction(NEW_ACCOUNT_VISIBLE);
950 intent.setPackage(desiredPackage);
951 intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
952 mContext.sendBroadcast(intent);
953 }
954
955 @Override
Dianne Hackborn164371f2013-10-01 19:10:13 -0700956 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
957 throws RemoteException {
958 try {
959 return super.onTransact(code, data, reply, flags);
960 } catch (RuntimeException e) {
961 // The account manager only throws security exceptions, so let's
962 // log all others.
963 if (!(e instanceof SecurityException)) {
964 Slog.wtf(TAG, "Account Manager Crash", e);
965 }
966 throw e;
967 }
968 }
969
Amith Yamasani258848d2012-08-10 17:06:33 -0700970 private UserManager getUserManager() {
971 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700972 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -0700973 }
974 return mUserManager;
975 }
976
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700977 /**
978 * Validate internal set of accounts against installed authenticators for
979 * given user. Clears cached authenticators before validating.
980 */
981 public void validateAccounts(int userId) {
982 final UserAccounts accounts = getUserAccounts(userId);
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700983 // Invalidate user-specific cache to make sure we catch any
984 // removed authenticators.
985 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
986 }
987
988 /**
989 * Validate internal set of accounts against installed authenticators for
990 * given user. Clear cached authenticators before validating when requested.
991 */
992 private void validateAccountsInternal(
993 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700994 if (Log.isLoggable(TAG, Log.DEBUG)) {
995 Log.d(TAG, "validateAccountsInternal " + accounts.userId
996 + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600997 + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700998 }
Carlos Valdiviaa46b1122016-04-26 19:36:50 -0700999
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001000 if (invalidateAuthenticatorCache) {
1001 mAuthenticatorCache.invalidateCache(accounts.userId);
1002 }
1003
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001004 final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
1005 mAuthenticatorCache, accounts.userId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001006 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001007
Amith Yamasani04e0d262012-02-14 11:50:53 -08001008 synchronized (accounts.cacheLock) {
1009 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001010 boolean accountDeleted = false;
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001011
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001012 // Get a map of stored authenticator types to UID
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001013 Map<String, Integer> metaAuthUid = DeDatabaseHelper.findMetaAuthUid(db);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001014 // Create a list of authenticator type whose previous uid no longer exists
1015 HashSet<String> obsoleteAuthType = Sets.newHashSet();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001016 SparseBooleanArray knownUids = null;
1017 for (Entry<String, Integer> authToUidEntry : metaAuthUid.entrySet()) {
1018 String type = authToUidEntry.getKey();
1019 int uid = authToUidEntry.getValue();
1020 Integer knownUid = knownAuth.get(type);
1021 if (knownUid != null && uid == knownUid) {
1022 // Remove it from the knownAuth list if it's unchanged.
1023 knownAuth.remove(type);
1024 } else {
1025 /*
1026 * The authenticator is presently not cached and should only be triggered
1027 * when we think an authenticator has been removed (or is being updated).
1028 * But we still want to check if any data with the associated uid is
1029 * around. This is an (imperfect) signal that the package may be updating.
1030 *
1031 * A side effect of this is that an authenticator sharing a uid with
1032 * multiple apps won't get its credentials wiped as long as some app with
1033 * that uid is still on the device. But I suspect that this is a rare case.
1034 * And it isn't clear to me how an attacker could really exploit that
1035 * feature.
1036 *
1037 * The upshot is that we don't have to worry about accounts getting
1038 * uninstalled while the authenticator's package is being updated.
1039 *
1040 */
1041 if (knownUids == null) {
1042 knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001043 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001044 if (!knownUids.get(uid)) {
1045 // The authenticator is not presently available to the cache. And the
1046 // package no longer has a data directory (so we surmise it isn't updating).
1047 // So purge its data from the account databases.
1048 obsoleteAuthType.add(type);
1049 // And delete it from the TABLE_META
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001050 DeDatabaseHelper.deleteMetaByAuthTypeAndUid(db, type, uid);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001051 }
1052 }
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001053 }
1054
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001055 // Add the newly registered authenticator to TABLE_META. If old authenticators have
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001056 // been re-enabled (after being updated for example), then we just overwrite the old
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001057 // values.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001058 for (Entry<String, Integer> entry : knownAuth.entrySet()) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001059 DeDatabaseHelper.insertOrReplaceMetaAuthTypeAndUid(db, entry.getKey(),
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001060 entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001061 }
1062
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001063 final Map<Long, Account> accountsMap = DeDatabaseHelper.findAllAccounts(db);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001064 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001065 accounts.accountCache.clear();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001066 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001067 for (Entry<Long, Account> accountEntry : accountsMap.entrySet()) {
1068 final long accountId = accountEntry.getKey();
1069 final Account account = accountEntry.getValue();
1070 if (obsoleteAuthType.contains(account.type)) {
1071 Slog.w(TAG, "deleting account " + account.name + " because type "
1072 + account.type + "'s registered authenticator no longer exist.");
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001073 db.beginTransaction();
1074 try {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001075 DeDatabaseHelper.deleteAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001076 // Also delete from CE table if user is unlocked; if user is currently
1077 // locked the account will be removed later by syncDeCeAccountsLocked
1078 if (userUnlocked) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001079 AccountsDb.deleteCeAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001080 }
1081 db.setTransactionSuccessful();
1082 } finally {
1083 db.endTransaction();
1084 }
Fred Quintana56285a62010-12-02 14:20:51 -08001085 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001086
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001087 logRecord(AccountsDb.DEBUG_ACTION_AUTHENTICATOR_REMOVE,
1088 AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001089
Amith Yamasani04e0d262012-02-14 11:50:53 -08001090 accounts.userDataCache.remove(account);
1091 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001092 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08001093 } else {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001094 ArrayList<String> accountNames = accountNamesByType.get(account.type);
Fred Quintana56285a62010-12-02 14:20:51 -08001095 if (accountNames == null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001096 accountNames = new ArrayList<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001097 accountNamesByType.put(account.type, accountNames);
Fred Quintana56285a62010-12-02 14:20:51 -08001098 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001099 accountNames.add(account.name);
Fred Quintana56285a62010-12-02 14:20:51 -08001100 }
1101 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001102 for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -08001103 final String accountType = cur.getKey();
1104 final ArrayList<String> accountNames = cur.getValue();
1105 final Account[] accountsForType = new Account[accountNames.size()];
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001106 for (int i = 0; i < accountsForType.length; i++) {
Svet Ganovf6d424f12016-09-20 20:18:53 -07001107 accountsForType[i] = new Account(accountNames.get(i), accountType,
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07001108 UUID.randomUUID().toString());
Fred Quintana56285a62010-12-02 14:20:51 -08001109 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001110 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -08001111 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001112 } finally {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001113 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001114 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001115 }
Fred Quintanaafa92b82009-12-01 16:27:03 -08001116 }
1117 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001118 }
1119
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001120 private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
1121 // Get the UIDs of all apps that might have data on the device. We want
1122 // to preserve user data if the app might otherwise be storing data.
1123 List<PackageInfo> pkgsWithData =
1124 mPackageManager.getInstalledPackagesAsUser(
1125 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
1126 SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
1127 for (PackageInfo pkgInfo : pkgsWithData) {
1128 if (pkgInfo.applicationInfo != null
1129 && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
1130 knownUids.put(pkgInfo.applicationInfo.uid, true);
1131 }
1132 }
1133 return knownUids;
1134 }
1135
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001136 static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001137 Context context,
1138 int userId) {
1139 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001140 return getAuthenticatorTypeAndUIDForUser(authCache, userId);
1141 }
1142
1143 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1144 IAccountAuthenticatorCache authCache,
1145 int userId) {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001146 HashMap<String, Integer> knownAuth = new HashMap<>();
1147 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
1148 .getAllServices(userId)) {
1149 knownAuth.put(service.type.type, service.uid);
1150 }
1151 return knownAuth;
1152 }
1153
Amith Yamasani04e0d262012-02-14 11:50:53 -08001154 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001155 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001156 }
1157
1158 protected UserAccounts getUserAccounts(int userId) {
1159 synchronized (mUsers) {
1160 UserAccounts accounts = mUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001161 boolean validateAccounts = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001162 if (accounts == null) {
Fyodor Kupolovda993802016-09-21 14:47:10 -07001163 File preNDbFile = new File(mInjector.getPreNDatabaseName(userId));
1164 File deDbFile = new File(mInjector.getDeDatabaseName(userId));
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001165 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001166 initializeDebugDbSizeAndCompileSqlStatementForLogging(
1167 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001168 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001169 purgeOldGrants(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001170 validateAccounts = true;
1171 }
1172 // open CE database if necessary
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001173 if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001174 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
1175 synchronized (accounts.cacheLock) {
Fyodor Kupolovda993802016-09-21 14:47:10 -07001176 File preNDatabaseFile = new File(mInjector.getPreNDatabaseName(userId));
1177 File ceDatabaseFile = new File(mInjector.getCeDatabaseName(userId));
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001178 CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
1179 accounts.openHelper.attachCeDatabase(ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001180 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001181 syncDeCeAccountsLocked(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001182 }
1183 if (validateAccounts) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001184 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001185 }
1186 return accounts;
1187 }
1188 }
1189
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001190 private void syncDeCeAccountsLocked(UserAccounts accounts) {
1191 Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
1192 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001193 List<Account> accountsToRemove = AccountsDb.findCeAccountsNotInDe(db);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001194 if (!accountsToRemove.isEmpty()) {
1195 Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
1196 + accounts.userId + " was locked. Removing accounts from CE tables");
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001197 logRecord(accounts, AccountsDb.DEBUG_ACTION_SYNC_DE_CE_ACCOUNTS,
1198 AccountsDb.TABLE_ACCOUNTS);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001199
1200 for (Account account : accountsToRemove) {
1201 removeAccountInternal(accounts, account, Process.myUid());
1202 }
1203 }
1204 }
1205
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001206 private void purgeOldGrantsAll() {
1207 synchronized (mUsers) {
1208 for (int i = 0; i < mUsers.size(); i++) {
1209 purgeOldGrants(mUsers.valueAt(i));
1210 }
1211 }
1212 }
1213
1214 private void purgeOldGrants(UserAccounts accounts) {
1215 synchronized (accounts.cacheLock) {
1216 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001217 List<Integer> uids = DeDatabaseHelper.findAllUidGrants(db);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001218 for (int uid : uids) {
1219 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
1220 if (packageExists) {
1221 continue;
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001222 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001223 Log.d(TAG, "deleting grants for UID " + uid
1224 + " because its package is no longer installed");
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001225 DeDatabaseHelper.deleteGrantsByUid(db, uid);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001226 }
1227 }
1228 }
1229
Amith Yamasani13593602012-03-22 16:16:17 -07001230 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -07001231 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -07001232 if (userId < 1) return;
1233
1234 UserAccounts accounts;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001235 boolean userUnlocked;
Amith Yamasani13593602012-03-22 16:16:17 -07001236 synchronized (mUsers) {
1237 accounts = mUsers.get(userId);
1238 mUsers.remove(userId);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001239 userUnlocked = mLocalUnlockedUsers.get(userId);
1240 mLocalUnlockedUsers.delete(userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001241 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001242 if (accounts != null) {
1243 synchronized (accounts.cacheLock) {
1244 accounts.openHelper.close();
1245 }
Amith Yamasani13593602012-03-22 16:16:17 -07001246 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001247 Log.i(TAG, "Removing database files for user " + userId);
Fyodor Kupolovda993802016-09-21 14:47:10 -07001248 File dbFile = new File(mInjector.getDeDatabaseName(userId));
Amith Yamasani13593602012-03-22 16:16:17 -07001249
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001250 AccountsDb.deleteDbFileWarnIfFailed(dbFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001251 // Remove CE file if user is unlocked, or FBE is not enabled
1252 boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
1253 if (!fbeEnabled || userUnlocked) {
Fyodor Kupolovda993802016-09-21 14:47:10 -07001254 File ceDb = new File(mInjector.getCeDatabaseName(userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001255 if (ceDb.exists()) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001256 AccountsDb.deleteDbFileWarnIfFailed(ceDb);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001257 }
1258 }
1259 }
1260
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001261 @VisibleForTesting
1262 void onUserUnlocked(Intent intent) {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -06001263 onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
1264 }
1265
1266 void onUnlockUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001267 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1268 Log.v(TAG, "onUserUnlocked " + userId);
1269 }
1270 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001271 mLocalUnlockedUsers.put(userId, true);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001272 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001273 if (userId < 1) return;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001274 syncSharedAccounts(userId);
1275 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001276
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001277 private void syncSharedAccounts(int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001278 // Check if there's a shared account that needs to be created as an account
1279 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
1280 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -07001281 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001282 int parentUserId = UserManager.isSplitSystemUser()
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001283 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001284 : UserHandle.USER_SYSTEM;
1285 if (parentUserId < 0) {
1286 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
1287 return;
1288 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001289 for (Account sa : sharedAccounts) {
1290 if (ArrayUtils.contains(accounts, sa)) continue;
1291 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001292 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001293 }
1294 }
1295
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001296 @Override
1297 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001298 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -07001299 }
1300
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001301 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001302 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001303 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001304 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1305 Log.v(TAG, "getPassword: " + account
1306 + ", caller's uid " + Binder.getCallingUid()
1307 + ", pid " + Binder.getCallingPid());
1308 }
Fred Quintana382601f2010-03-25 12:25:10 -07001309 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001310 int userId = UserHandle.getCallingUserId();
1311 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001312 String msg = String.format(
1313 "uid %s cannot get secrets for accounts of type: %s",
1314 callingUid,
1315 account.type);
1316 throw new SecurityException(msg);
1317 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001318 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001319 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001320 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001321 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001322 } finally {
1323 restoreCallingIdentity(identityToken);
1324 }
1325 }
1326
Amith Yamasani04e0d262012-02-14 11:50:53 -08001327 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -07001328 if (account == null) {
1329 return null;
1330 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001331 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001332 Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
1333 return null;
1334 }
Fred Quintana31957f12009-10-21 13:43:10 -07001335
Amith Yamasani04e0d262012-02-14 11:50:53 -08001336 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001337 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001338 return CeDatabaseHelper.findAccountPasswordByNameAndType(db, account.name,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001339 account.type);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001340 }
1341 }
1342
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001343 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001344 public String getPreviousName(Account account) {
1345 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1346 Log.v(TAG, "getPreviousName: " + account
1347 + ", caller's uid " + Binder.getCallingUid()
1348 + ", pid " + Binder.getCallingPid());
1349 }
1350 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001351 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001352 long identityToken = clearCallingIdentity();
1353 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001354 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001355 return readPreviousNameInternal(accounts, account);
1356 } finally {
1357 restoreCallingIdentity(identityToken);
1358 }
1359 }
1360
1361 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
1362 if (account == null) {
1363 return null;
1364 }
1365 synchronized (accounts.cacheLock) {
1366 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
1367 if (previousNameRef == null) {
1368 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001369 String previousName = DeDatabaseHelper.findAccountPreviousName(db, account);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001370 previousNameRef = new AtomicReference<>(previousName);
1371 accounts.previousNameCache.put(account, previousNameRef);
1372 return previousName;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001373 } else {
1374 return previousNameRef.get();
1375 }
1376 }
1377 }
1378
1379 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001380 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001381 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001382 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001383 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
1384 account, key, callingUid, Binder.getCallingPid());
1385 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -08001386 }
Fred Quintana382601f2010-03-25 12:25:10 -07001387 if (account == null) throw new IllegalArgumentException("account is null");
1388 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001389 int userId = UserHandle.getCallingUserId();
1390 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001391 String msg = String.format(
1392 "uid %s cannot get user data for accounts of type: %s",
1393 callingUid,
1394 account.type);
1395 throw new SecurityException(msg);
1396 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001397 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07001398 Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
1399 return null;
1400 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001401 long identityToken = clearCallingIdentity();
1402 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001403 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00001404 synchronized (accounts.cacheLock) {
1405 if (!accountExistsCacheLocked(accounts, account)) {
1406 return null;
1407 }
1408 return readUserDataInternalLocked(accounts, account, key);
1409 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001410 } finally {
1411 restoreCallingIdentity(identityToken);
1412 }
1413 }
1414
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001415 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001416 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001417 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001418 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1419 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001420 + "for user id " + userId
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001421 + " caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001422 + ", pid " + Binder.getCallingPid());
1423 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001424 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001425 if (isCrossUser(callingUid, userId)) {
1426 throw new SecurityException(
1427 String.format(
1428 "User %s tying to get authenticator types for %s" ,
1429 UserHandle.getCallingUserId(),
1430 userId));
1431 }
1432
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001433 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001434 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001435 return getAuthenticatorTypesInternal(userId);
1436
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001437 } finally {
1438 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001439 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001440 }
1441
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001442 /**
1443 * Should only be called inside of a clearCallingIdentity block.
1444 */
1445 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
Fyodor Kupolov81446482016-08-24 11:27:49 -07001446 mAuthenticatorCache.updateServices(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001447 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
1448 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
1449 AuthenticatorDescription[] types =
1450 new AuthenticatorDescription[authenticatorCollection.size()];
1451 int i = 0;
1452 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
1453 : authenticatorCollection) {
1454 types[i] = authenticator.type;
1455 i++;
1456 }
1457 return types;
1458 }
1459
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001460 private boolean isCrossUser(int callingUid, int userId) {
1461 return (userId != UserHandle.getCallingUserId()
1462 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001463 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001464 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1465 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001466 }
1467
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001468 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07001469 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001470 Bundle.setDefusable(extras, true);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001471 // clears the visible list functionality for this account because this method allows
1472 // default account access to all applications for account.
1473
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001474 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001475 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001476 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001477 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001478 + ", pid " + Binder.getCallingPid());
1479 }
Fred Quintana382601f2010-03-25 12:25:10 -07001480 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001481 int userId = UserHandle.getCallingUserId();
1482 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001483 String msg = String.format(
1484 "uid %s cannot explicitly add accounts of type: %s",
1485 callingUid,
1486 account.type);
1487 throw new SecurityException(msg);
1488 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001489 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001490 /*
1491 * Child users are not allowed to add accounts. Only the accounts that are
1492 * shared by the parent profile can be added to child profile.
1493 *
1494 * TODO: Only allow accounts that were shared to be added by
1495 * a limited user.
1496 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001497
Fred Quintana60307342009-03-24 22:48:12 -07001498 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001499 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001500 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001501 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001502 return addAccountInternal(accounts, account, password, extras, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -07001503 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001504 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001505 }
1506 }
1507
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001508 @Override
1509 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001510 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001511 int callingUid = Binder.getCallingUid();
1512 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1513 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001514 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001515 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001516 final UserAccounts fromAccounts = getUserAccounts(userFrom);
1517 final UserAccounts toAccounts = getUserAccounts(userTo);
1518 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001519 if (response != null) {
1520 Bundle result = new Bundle();
1521 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1522 try {
1523 response.onResult(result);
1524 } catch (RemoteException e) {
1525 Slog.w(TAG, "Failed to report error back to the client." + e);
1526 }
1527 }
1528 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -08001529 }
1530
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001531 Slog.d(TAG, "Copying account " + account.name
1532 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001533 long identityToken = clearCallingIdentity();
1534 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001535 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001536 false /* stripAuthTokenFromResult */, account.name,
1537 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001538 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001539 protected String toDebugString(long now) {
1540 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1541 + ", " + account.type;
1542 }
1543
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001544 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001545 public void run() throws RemoteException {
1546 mAuthenticator.getAccountCredentialsForCloning(this, account);
1547 }
1548
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001549 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001550 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001551 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001552 if (result != null
1553 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1554 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001555 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001556 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001557 super.onResult(result);
1558 }
1559 }
1560 }.bind();
1561 } finally {
1562 restoreCallingIdentity(identityToken);
1563 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001564 }
1565
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001566 @Override
1567 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001568 final int callingUid = Binder.getCallingUid();
1569 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1570 String msg = String.format(
1571 "accountAuthenticated( account: %s, callerUid: %s)",
1572 account,
1573 callingUid);
1574 Log.v(TAG, msg);
1575 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001576 if (account == null) {
1577 throw new IllegalArgumentException("account is null");
1578 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001579 int userId = UserHandle.getCallingUserId();
1580 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001581 String msg = String.format(
1582 "uid %s cannot notify authentication for accounts of type: %s",
1583 callingUid,
1584 account.type);
1585 throw new SecurityException(msg);
1586 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001587
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001588 if (!canUserModifyAccounts(userId, callingUid) ||
1589 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001590 return false;
1591 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001592
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001593 long identityToken = clearCallingIdentity();
1594 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001595 UserAccounts accounts = getUserAccounts(userId);
1596 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001597 } finally {
1598 restoreCallingIdentity(identityToken);
1599 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07001600 }
1601
1602 private boolean updateLastAuthenticatedTime(Account account) {
1603 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001604 synchronized (accounts.cacheLock) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001605 return DeDatabaseHelper.updateAccountLastAuthenticatedTime(
1606 accounts.openHelper.getWritableDatabase(), account);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001607 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001608 }
1609
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001610 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001611 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1612 final int parentUserId){
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001613 Bundle.setDefusable(accountCredentials, true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001614 long id = clearCallingIdentity();
1615 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001616 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001617 false /* stripAuthTokenFromResult */, account.name,
1618 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001619 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001620 protected String toDebugString(long now) {
1621 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1622 + ", " + account.type;
1623 }
1624
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001625 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001626 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001627 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001628 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001629 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -07001630 for (Account acc : getAccounts(parentUserId,
1631 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001632 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001633 mAuthenticator.addAccountFromCredentials(
1634 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001635 break;
1636 }
1637 }
1638 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001639 }
1640
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001641 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001642 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001643 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001644 // TODO: Anything to do if if succedded?
1645 // TODO: If it failed: Show error notification? Should we remove the shadow
1646 // account to avoid retries?
1647 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001648 }
1649
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001650 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001651 public void onError(int errorCode, String errorMessage) {
1652 super.onError(errorCode, errorMessage);
1653 // TODO: Show error notification to user
1654 // TODO: Should we remove the shadow account so that it doesn't keep trying?
1655 }
1656
1657 }.bind();
1658 } finally {
1659 restoreCallingIdentity(id);
1660 }
1661 }
1662
Amith Yamasani04e0d262012-02-14 11:50:53 -08001663 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001664 Bundle extras, int callingUid) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001665 Bundle.setDefusable(extras, true);
Fred Quintana743dfad2010-07-15 10:59:25 -07001666 if (account == null) {
1667 return false;
1668 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001669 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001670 Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
1671 + " is locked. callingUid=" + callingUid);
1672 return false;
1673 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001674 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001675 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001676 db.beginTransaction();
1677 try {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001678 if (CeDatabaseHelper.findAccountId(db, account) >= 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001679 Log.w(TAG, "insertAccountIntoDatabase: " + account
1680 + ", skipping since the account already exists");
1681 return false;
1682 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001683 long accountId = CeDatabaseHelper.insertAccount(db, account, password);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001684 if (accountId < 0) {
1685 Log.w(TAG, "insertAccountIntoDatabase: " + account
1686 + ", skipping the DB insert failed");
1687 return false;
1688 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001689 // Insert into DE table
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001690 if (DeDatabaseHelper.insertAccount(db, account, accountId) < 0) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001691 Log.w(TAG, "insertAccountIntoDatabase: " + account
1692 + ", skipping the DB insert failed");
1693 return false;
1694 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001695 if (extras != null) {
1696 for (String key : extras.keySet()) {
1697 final String value = extras.getString(key);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001698 if (CeDatabaseHelper.insertExtra(db, accountId, key, value) < 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001699 Log.w(TAG, "insertAccountIntoDatabase: " + account
1700 + ", skipping since insertExtra failed for key " + key);
1701 return false;
1702 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001703 }
1704 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001705 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001706
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001707 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
1708 accountId, accounts, callingUid);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001709
Amith Yamasani04e0d262012-02-14 11:50:53 -08001710 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001711 } finally {
1712 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001713 }
Amith Yamasani5be347b2013-03-31 17:44:31 -07001714 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001715 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1716 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001717 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001718
1719 // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1720 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001721 return true;
1722 }
1723
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001724 private boolean isLocalUnlockedUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001725 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001726 return mLocalUnlockedUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001727 }
1728 }
1729
Amith Yamasani5be347b2013-03-31 17:44:31 -07001730 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001731 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -07001732 * running, then clone the account too.
1733 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001734 *
Amith Yamasani5be347b2013-03-31 17:44:31 -07001735 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001736 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -07001737 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -07001738 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001739 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001740 addSharedAccountAsUser(account, user.id);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001741 if (isLocalUnlockedUser(user.id)) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07001742 mHandler.sendMessage(mHandler.obtainMessage(
Fyodor Kupolov041232a2016-02-22 15:01:45 -08001743 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -07001744 }
1745 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001746 }
1747 }
1748
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001749 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001750 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001751 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001752 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001753 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1754 Log.v(TAG, "hasFeatures: " + account
1755 + ", response " + response
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001756 + ", features " + Arrays.toString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001757 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001758 + ", pid " + Binder.getCallingPid());
1759 }
Fred Quintana382601f2010-03-25 12:25:10 -07001760 if (response == null) throw new IllegalArgumentException("response is null");
1761 if (account == null) throw new IllegalArgumentException("account is null");
1762 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001763 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001764 checkReadAccountsPermitted(callingUid, account.type, userId,
1765 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001766
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001767 long identityToken = clearCallingIdentity();
1768 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001769 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001770 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001771 } finally {
1772 restoreCallingIdentity(identityToken);
1773 }
1774 }
1775
1776 private class TestFeaturesSession extends Session {
1777 private final String[] mFeatures;
1778 private final Account mAccount;
1779
Amith Yamasani04e0d262012-02-14 11:50:53 -08001780 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001781 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001782 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001783 true /* stripAuthTokenFromResult */, account.name,
1784 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001785 mFeatures = features;
1786 mAccount = account;
1787 }
1788
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001789 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001790 public void run() throws RemoteException {
1791 try {
1792 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1793 } catch (RemoteException e) {
1794 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1795 }
1796 }
1797
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001798 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001799 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001800 Bundle.setDefusable(result, true);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001801 IAccountManagerResponse response = getResponseAndClose();
1802 if (response != null) {
1803 try {
1804 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001805 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001806 return;
1807 }
Fred Quintana56285a62010-12-02 14:20:51 -08001808 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1809 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1810 + response);
1811 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001812 final Bundle newResult = new Bundle();
1813 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1814 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1815 response.onResult(newResult);
1816 } catch (RemoteException e) {
1817 // if the caller is dead then there is no one to care about remote exceptions
1818 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1819 Log.v(TAG, "failure while notifying response", e);
1820 }
1821 }
1822 }
1823 }
1824
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001825 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001826 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001827 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001828 + ", " + mAccount
1829 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1830 }
1831 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001832
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001833 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001834 public void renameAccount(
1835 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001836 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001837 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1838 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001839 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001840 + ", pid " + Binder.getCallingPid());
1841 }
1842 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001843 int userId = UserHandle.getCallingUserId();
1844 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001845 String msg = String.format(
1846 "uid %s cannot rename accounts of type: %s",
1847 callingUid,
1848 accountToRename.type);
1849 throw new SecurityException(msg);
1850 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001851 long identityToken = clearCallingIdentity();
1852 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001853 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001854 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001855 Bundle result = new Bundle();
1856 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1857 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07001858 result.putString(AccountManager.KEY_ACCOUNT_ACCESS_ID,
1859 resultingAccount.getAccessId());
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001860 try {
1861 response.onResult(result);
1862 } catch (RemoteException e) {
1863 Log.w(TAG, e.getMessage());
1864 }
1865 } finally {
1866 restoreCallingIdentity(identityToken);
1867 }
1868 }
1869
1870 private Account renameAccountInternal(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001871 UserAccounts accounts, Account accountToRename, String newName) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001872 Account resultAccount = null;
1873 /*
1874 * Cancel existing notifications. Let authenticators
1875 * re-post notifications as required. But we don't know if
1876 * the authenticators have bound their notifications to
1877 * now stale account name data.
1878 *
1879 * With a rename api, we might not need to do this anymore but it
1880 * shouldn't hurt.
1881 */
1882 cancelNotification(
1883 getSigninRequiredNotificationId(accounts, accountToRename),
1884 new UserHandle(accounts.userId));
1885 synchronized(accounts.credentialsPermissionNotificationIds) {
1886 for (Pair<Pair<Account, String>, Integer> pair:
1887 accounts.credentialsPermissionNotificationIds.keySet()) {
1888 if (accountToRename.equals(pair.first.first)) {
1889 int id = accounts.credentialsPermissionNotificationIds.get(pair);
1890 cancelNotification(id, new UserHandle(accounts.userId));
1891 }
1892 }
1893 }
1894 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001895 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001896 db.beginTransaction();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001897 Account renamedAccount = new Account(newName, accountToRename.type);
1898 try {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001899 final long accountId = DeDatabaseHelper.findAccountId(db, accountToRename);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001900 if (accountId >= 0) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07001901 CeDatabaseHelper.renameAccount(db, accountId, newName);
1902 DeDatabaseHelper.renameAccount(db, accountId, newName, accountToRename.name);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001903 }
1904 } finally {
1905 db.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001906 }
1907 /*
1908 * Database transaction was successful. Clean up cached
1909 * data associated with the account in the user profile.
1910 */
Svet Ganov5c4a5122016-09-23 19:29:11 -07001911 renamedAccount = insertAccountIntoCacheLocked(accounts, renamedAccount);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001912 /*
1913 * Extract the data and token caches before removing the
1914 * old account to preserve the user data associated with
1915 * the account.
1916 */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001917 Map<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1918 Map<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001919 removeAccountFromCacheLocked(accounts, accountToRename);
1920 /*
1921 * Update the cached data associated with the renamed
1922 * account.
1923 */
1924 accounts.userDataCache.put(renamedAccount, tmpData);
1925 accounts.authTokenCache.put(renamedAccount, tmpTokens);
1926 accounts.previousNameCache.put(
1927 renamedAccount,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001928 new AtomicReference<>(accountToRename.name));
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001929 resultAccount = renamedAccount;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001930
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001931 int parentUserId = accounts.userId;
1932 if (canHaveProfile(parentUserId)) {
1933 /*
1934 * Owner or system user account was renamed, rename the account for
1935 * those users with which the account was shared.
1936 */
1937 List<UserInfo> users = getUserManager().getUsers(true);
1938 for (UserInfo user : users) {
1939 if (user.isRestricted()
1940 && (user.restrictedProfileParentId == parentUserId)) {
1941 renameSharedAccountAsUser(accountToRename, newName, user.id);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001942 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001943 }
1944 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001945 sendAccountsChangedBroadcast(accounts.userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001946 }
1947 return resultAccount;
1948 }
1949
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001950 private boolean canHaveProfile(final int parentUserId) {
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001951 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001952 return userInfo != null && userInfo.canHaveProfile();
1953 }
1954
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001955 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001956 public void removeAccount(IAccountManagerResponse response, Account account,
1957 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001958 removeAccountAsUser(
1959 response,
1960 account,
1961 expectActivityLaunch,
1962 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001963 }
1964
1965 @Override
1966 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001967 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001968 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001969 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1970 Log.v(TAG, "removeAccount: " + account
1971 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001972 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001973 + ", pid " + Binder.getCallingPid()
1974 + ", for user id " + userId);
1975 }
1976 if (response == null) throw new IllegalArgumentException("response is null");
1977 if (account == null) throw new IllegalArgumentException("account is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001978 // Only allow the system process to modify accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001979 if (isCrossUser(callingUid, userId)) {
1980 throw new SecurityException(
1981 String.format(
1982 "User %s tying remove account for %s" ,
1983 UserHandle.getCallingUserId(),
1984 userId));
1985 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001986 /*
1987 * Only the system or authenticator should be allowed to remove accounts for that
1988 * authenticator. This will let users remove accounts (via Settings in the system) but not
1989 * arbitrary applications (like competing authenticators).
1990 */
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001991 UserHandle user = UserHandle.of(userId);
Ian Pedowitz358e51f2016-03-15 17:08:27 +00001992 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
1993 && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001994 String msg = String.format(
1995 "uid %s cannot remove accounts of type: %s",
1996 callingUid,
1997 account.type);
1998 throw new SecurityException(msg);
1999 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002000 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002001 try {
2002 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2003 "User cannot modify accounts");
2004 } catch (RemoteException re) {
2005 }
2006 return;
2007 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002008 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002009 try {
2010 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2011 "User cannot modify accounts of this type (policy).");
2012 } catch (RemoteException re) {
2013 }
2014 return;
2015 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002016 long identityToken = clearCallingIdentity();
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002017 UserAccounts accounts = getUserAccounts(userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002018 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002019 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002020 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08002021 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002022 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002023 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002024 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002025 }
2026 }
2027 }
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002028 SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002029 final long accountId = DeDatabaseHelper.findAccountId(db, account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002030 logRecord(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002031 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
2032 AccountsDb.TABLE_ACCOUNTS,
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002033 accountId,
2034 accounts,
2035 callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002036 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002037 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
2038 } finally {
2039 restoreCallingIdentity(identityToken);
2040 }
2041 }
2042
2043 @Override
2044 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002045 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002046 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2047 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002048 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002049 + ", pid " + Binder.getCallingPid());
2050 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002051 int userId = Binder.getCallingUserHandle().getIdentifier();
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002052 if (account == null) {
2053 /*
2054 * Null accounts should result in returning false, as per
2055 * AccountManage.addAccountExplicitly(...) java doc.
2056 */
2057 Log.e(TAG, "account is null");
2058 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002059 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002060 String msg = String.format(
2061 "uid %s cannot explicitly add accounts of type: %s",
2062 callingUid,
2063 account.type);
2064 throw new SecurityException(msg);
2065 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07002066 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002067 UserAccounts accounts = getUserAccountsForCaller();
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002068 SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002069 final long accountId = DeDatabaseHelper.findAccountId(db, account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002070 logRecord(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002071 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
2072 AccountsDb.TABLE_ACCOUNTS,
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002073 accountId,
2074 accounts,
2075 callingUid);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002076 long identityToken = clearCallingIdentity();
2077 try {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002078 return removeAccountInternal(accounts, account, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002079 } finally {
2080 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07002081 }
Fred Quintana60307342009-03-24 22:48:12 -07002082 }
2083
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002084 private class RemoveAccountSession extends Session {
2085 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002086 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002087 Account account, boolean expectActivityLaunch) {
2088 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002089 true /* stripAuthTokenFromResult */, account.name,
2090 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002091 mAccount = account;
2092 }
2093
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002094 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002095 protected String toDebugString(long now) {
2096 return super.toDebugString(now) + ", removeAccount"
2097 + ", account " + mAccount;
2098 }
2099
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002100 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002101 public void run() throws RemoteException {
2102 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
2103 }
2104
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002105 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002106 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002107 Bundle.setDefusable(result, true);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002108 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
2109 && !result.containsKey(AccountManager.KEY_INTENT)) {
2110 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002111 if (removalAllowed) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002112 removeAccountInternal(mAccounts, mAccount, getCallingUid());
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002113 }
2114 IAccountManagerResponse response = getResponseAndClose();
2115 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08002116 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2117 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2118 + response);
2119 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002120 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002121 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002122 try {
2123 response.onResult(result2);
2124 } catch (RemoteException e) {
2125 // ignore
2126 }
2127 }
2128 }
2129 super.onResult(result);
2130 }
2131 }
2132
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07002133 @VisibleForTesting
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002134 protected void removeAccountInternal(Account account) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002135 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
Amith Yamasani04e0d262012-02-14 11:50:53 -08002136 }
2137
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002138 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002139 boolean isChanged = false;
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002140 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002141 if (!userUnlocked) {
2142 Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
2143 + " is still locked. CE data will be removed later");
2144 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002145 synchronized (accounts.cacheLock) {
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002146 final SQLiteDatabase db = userUnlocked
2147 ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
2148 : accounts.openHelper.getWritableDatabase();
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002149 db.beginTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002150 // Set to a dummy value, this will only be used if the database
2151 // transaction succeeds.
2152 long accountId = -1;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002153 try {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002154 accountId = DeDatabaseHelper.findAccountId(db, account);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002155 if (accountId >= 0) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002156 DeDatabaseHelper.deleteAccount(db, accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002157 if (userUnlocked) {
2158 // Delete from CE table
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002159 AccountsDb.deleteCeAccount(db, accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002160 }
2161 db.setTransactionSuccessful();
2162 isChanged = true;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002163 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002164 } finally {
2165 db.endTransaction();
2166 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002167 if (isChanged) {
2168 removeAccountFromCacheLocked(accounts, account);
2169 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
2170 sendAccountsChangedBroadcast(accounts.userId);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002171 String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
2172 : AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
2173 logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002174 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002175 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002176 long id = Binder.clearCallingIdentity();
2177 try {
2178 int parentUserId = accounts.userId;
2179 if (canHaveProfile(parentUserId)) {
2180 // Remove from any restricted profiles that are sharing this account.
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002181 List<UserInfo> users = getUserManager().getUsers(true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002182 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002183 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002184 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002185 }
2186 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08002187 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002188 } finally {
2189 Binder.restoreCallingIdentity(id);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002190 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002191
2192 if (isChanged) {
2193 synchronized (accounts.credentialsPermissionNotificationIds) {
2194 for (Pair<Pair<Account, String>, Integer> key
2195 : accounts.credentialsPermissionNotificationIds.keySet()) {
2196 if (account.equals(key.first.first)
Svet Ganovf6d424f12016-09-20 20:18:53 -07002197 && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002198 final int uid = (Integer) key.second;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07002199 mHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002200 account, uid, false));
2201 }
2202 }
2203 }
2204 }
2205
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002206 return isChanged;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002207 }
2208
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002209 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002210 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002211 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002212 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2213 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07002214 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08002215 + ", pid " + Binder.getCallingPid());
2216 }
Fred Quintana382601f2010-03-25 12:25:10 -07002217 if (accountType == null) throw new IllegalArgumentException("accountType is null");
2218 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002219 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002220 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002221 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002222 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002223 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002224 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002225 db.beginTransaction();
2226 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002227 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002228 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002229 db.setTransactionSuccessful();
2230 } finally {
2231 db.endTransaction();
2232 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002233 }
Fred Quintana60307342009-03-24 22:48:12 -07002234 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002235 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002236 }
2237 }
2238
Carlos Valdivia91979be2015-05-22 14:11:35 -07002239 private void invalidateCustomTokenLocked(
2240 UserAccounts accounts,
2241 String accountType,
2242 String authToken) {
2243 if (authToken == null || accountType == null) {
2244 return;
2245 }
2246 // Also wipe out cached token in memory.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002247 accounts.accountTokenCaches.remove(accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002248 }
2249
Amith Yamasani04e0d262012-02-14 11:50:53 -08002250 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
2251 String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002252 if (authToken == null || accountType == null) {
2253 return;
2254 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002255 Cursor cursor = CeDatabaseHelper.findAuthtokenForAllAccounts(db, accountType, authToken);
Fred Quintana33269202009-04-20 16:05:10 -07002256 try {
2257 while (cursor.moveToNext()) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002258 String authTokenId = cursor.getString(0);
Fred Quintana33269202009-04-20 16:05:10 -07002259 String accountName = cursor.getString(1);
2260 String authTokenType = cursor.getString(2);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002261 CeDatabaseHelper.deleteAuthToken(db, authTokenId);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002262 writeAuthTokenIntoCacheLocked(
2263 accounts,
2264 db,
2265 new Account(accountName, accountType),
2266 authTokenType,
2267 null);
Fred Quintana60307342009-03-24 22:48:12 -07002268 }
Fred Quintana33269202009-04-20 16:05:10 -07002269 } finally {
2270 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07002271 }
2272 }
2273
Carlos Valdivia91979be2015-05-22 14:11:35 -07002274 private void saveCachedToken(
2275 UserAccounts accounts,
2276 Account account,
2277 String callerPkg,
2278 byte[] callerSigDigest,
2279 String tokenType,
2280 String token,
2281 long expiryMillis) {
2282
2283 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
2284 return;
2285 }
2286 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002287 UserHandle.of(accounts.userId));
Carlos Valdivia91979be2015-05-22 14:11:35 -07002288 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002289 accounts.accountTokenCaches.put(
2290 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002291 }
2292 }
2293
Amith Yamasani04e0d262012-02-14 11:50:53 -08002294 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
2295 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07002296 if (account == null || type == null) {
2297 return false;
2298 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002299 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002300 UserHandle.of(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002301 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002302 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002303 db.beginTransaction();
2304 try {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002305 long accountId = DeDatabaseHelper.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002306 if (accountId < 0) {
2307 return false;
2308 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002309 CeDatabaseHelper.deleteAuthtokensByAccountIdAndType(db, accountId, type);
2310 if (CeDatabaseHelper.insertAuthToken(db, accountId, type, authToken) >= 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002311 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002312 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002313 return true;
2314 }
Fred Quintana33269202009-04-20 16:05:10 -07002315 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002316 } finally {
2317 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07002318 }
Fred Quintana60307342009-03-24 22:48:12 -07002319 }
2320 }
2321
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002322 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002323 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002324 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002325 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2326 Log.v(TAG, "peekAuthToken: " + account
2327 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002328 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002329 + ", pid " + Binder.getCallingPid());
2330 }
Fred Quintana382601f2010-03-25 12:25:10 -07002331 if (account == null) throw new IllegalArgumentException("account is null");
2332 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002333 int userId = UserHandle.getCallingUserId();
2334 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002335 String msg = String.format(
2336 "uid %s cannot peek the authtokens associated with accounts of type: %s",
2337 callingUid,
2338 account.type);
2339 throw new SecurityException(msg);
2340 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002341 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07002342 Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
2343 + callingUid);
2344 return null;
2345 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002346 long identityToken = clearCallingIdentity();
2347 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002348 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002349 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002350 } finally {
2351 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002352 }
Fred Quintana60307342009-03-24 22:48:12 -07002353 }
2354
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002355 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002356 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002357 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002358 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2359 Log.v(TAG, "setAuthToken: " + account
2360 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002361 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002362 + ", pid " + Binder.getCallingPid());
2363 }
Fred Quintana382601f2010-03-25 12:25:10 -07002364 if (account == null) throw new IllegalArgumentException("account is null");
2365 if (authTokenType == null) throw new IllegalArgumentException("authTokenType 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 auth tokens associated with 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);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002377 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002378 } finally {
2379 restoreCallingIdentity(identityToken);
2380 }
Fred Quintana60307342009-03-24 22:48:12 -07002381 }
2382
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002383 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002384 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002385 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002386 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2387 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002388 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002389 + ", pid " + Binder.getCallingPid());
2390 }
Fred Quintana382601f2010-03-25 12:25:10 -07002391 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002392 int userId = UserHandle.getCallingUserId();
2393 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002394 String msg = String.format(
2395 "uid %s cannot set secrets for accounts of type: %s",
2396 callingUid,
2397 account.type);
2398 throw new SecurityException(msg);
2399 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002400 long identityToken = clearCallingIdentity();
2401 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002402 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002403 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002404 } finally {
2405 restoreCallingIdentity(identityToken);
2406 }
Fred Quintana60307342009-03-24 22:48:12 -07002407 }
2408
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002409 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
2410 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07002411 if (account == null) {
2412 return;
2413 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002414 boolean isChanged = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002415 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002416 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002417 db.beginTransaction();
2418 try {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002419 final long accountId = DeDatabaseHelper.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002420 if (accountId >= 0) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002421 CeDatabaseHelper.updateAccountPassword(db, accountId, password);
2422 CeDatabaseHelper.deleteAuthTokensByAccountId(db, accountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002423 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002424 accounts.accountTokenCaches.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002425 db.setTransactionSuccessful();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002426 // If there is an account whose password will be updated and the database
2427 // transactions succeed, then we say that a change has occured. Even if the
2428 // new password is the same as the old and there were no authtokens to delete.
2429 isChanged = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002430 String action = (password == null || password.length() == 0) ?
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002431 AccountsDb.DEBUG_ACTION_CLEAR_PASSWORD
2432 : AccountsDb.DEBUG_ACTION_SET_PASSWORD;
2433 logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08002434 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002435 } finally {
2436 db.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002437 if (isChanged) {
2438 // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
2439 sendAccountsChangedBroadcast(accounts.userId);
2440 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002441 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002442 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07002443 }
2444
Amith Yamasani04e0d262012-02-14 11:50:53 -08002445 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08002446 Log.i(TAG, "the accounts changed, sending broadcast of "
2447 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07002448 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07002449 }
2450
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002451 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002452 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002453 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002454 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2455 Log.v(TAG, "clearPassword: " + account
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 (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002460 int userId = UserHandle.getCallingUserId();
2461 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002462 String msg = String.format(
2463 "uid %s cannot clear passwords for accounts of type: %s",
2464 callingUid,
2465 account.type);
2466 throw new SecurityException(msg);
2467 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002468 long identityToken = clearCallingIdentity();
2469 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002470 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002471 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002472 } finally {
2473 restoreCallingIdentity(identityToken);
2474 }
Fred Quintana60307342009-03-24 22:48:12 -07002475 }
2476
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002477 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002478 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002479 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002480 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2481 Log.v(TAG, "setUserData: " + account
2482 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002483 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002484 + ", pid " + Binder.getCallingPid());
2485 }
Fred Quintana382601f2010-03-25 12:25:10 -07002486 if (key == null) throw new IllegalArgumentException("key is null");
2487 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002488 int userId = UserHandle.getCallingUserId();
2489 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002490 String msg = String.format(
2491 "uid %s cannot set user data for accounts of type: %s",
2492 callingUid,
2493 account.type);
2494 throw new SecurityException(msg);
2495 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002496 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002497 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002498 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002499 synchronized (accounts.cacheLock) {
2500 if (!accountExistsCacheLocked(accounts, account)) {
2501 return;
2502 }
2503 setUserdataInternalLocked(accounts, account, key, value);
2504 }
Fred Quintana60307342009-03-24 22:48:12 -07002505 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002506 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002507 }
2508 }
2509
Simranjit Kohli858511c2016-03-10 18:36:11 +00002510 private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
2511 if (accounts.accountCache.containsKey(account.type)) {
2512 for (Account acc : accounts.accountCache.get(account.type)) {
2513 if (acc.name.equals(account.name)) {
2514 return true;
2515 }
2516 }
2517 }
2518 return false;
2519 }
2520
2521 private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
Amith Yamasani04e0d262012-02-14 11:50:53 -08002522 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07002523 if (account == null || key == null) {
2524 return;
2525 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00002526 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
2527 db.beginTransaction();
2528 try {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002529 long accountId = DeDatabaseHelper.findAccountId(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002530 if (accountId < 0) {
2531 return;
2532 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002533 long extrasId = CeDatabaseHelper.findExtrasIdByAccountId(db, accountId, key);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002534 if (extrasId < 0) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002535 extrasId = CeDatabaseHelper.insertExtra(db, accountId, key, value);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002536 if (extrasId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002537 return;
2538 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002539 } else if (!CeDatabaseHelper.updateExtra(db, extrasId, value)) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002540 return;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002541 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00002542 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
2543 db.setTransactionSuccessful();
2544 } finally {
2545 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002546 }
2547 }
2548
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002549 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08002550 if (result == null) {
2551 Log.e(TAG, "the result is unexpectedly null", new Exception());
2552 }
2553 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2554 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2555 + response);
2556 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002557 try {
2558 response.onResult(result);
2559 } catch (RemoteException e) {
2560 // if the caller is dead then there is no one to care about remote
2561 // exceptions
2562 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2563 Log.v(TAG, "failure while notifying response", e);
2564 }
2565 }
2566 }
2567
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002568 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07002569 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
2570 final String authTokenType)
2571 throws RemoteException {
2572 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08002573 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2574
Fred Quintanad9640ec2012-05-23 12:37:00 -07002575 final int callingUid = getCallingUid();
2576 clearCallingIdentity();
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07002577 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07002578 throw new SecurityException("can only call from system");
2579 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002580 int userId = UserHandle.getUserId(callingUid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002581 long identityToken = clearCallingIdentity();
2582 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002583 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002584 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
2585 false /* stripAuthTokenFromResult */, null /* accountName */,
2586 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002587 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002588 protected String toDebugString(long now) {
2589 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07002590 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08002591 + ", authTokenType " + authTokenType;
2592 }
2593
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002594 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002595 public void run() throws RemoteException {
2596 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2597 }
2598
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002599 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002600 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002601 Bundle.setDefusable(result, true);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002602 if (result != null) {
2603 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
2604 Bundle bundle = new Bundle();
2605 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
2606 super.onResult(bundle);
2607 return;
2608 } else {
2609 super.onResult(result);
2610 }
2611 }
2612 }.bind();
2613 } finally {
2614 restoreCallingIdentity(identityToken);
2615 }
2616 }
2617
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002618 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07002619 public void getAuthToken(
2620 IAccountManagerResponse response,
2621 final Account account,
2622 final String authTokenType,
2623 final boolean notifyOnAuthFailure,
2624 final boolean expectActivityLaunch,
2625 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002626 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002627 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2628 Log.v(TAG, "getAuthToken: " + account
2629 + ", response " + response
2630 + ", authTokenType " + authTokenType
2631 + ", notifyOnAuthFailure " + notifyOnAuthFailure
2632 + ", expectActivityLaunch " + expectActivityLaunch
2633 + ", caller's uid " + Binder.getCallingUid()
2634 + ", pid " + Binder.getCallingPid());
2635 }
Fred Quintana382601f2010-03-25 12:25:10 -07002636 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002637 try {
2638 if (account == null) {
2639 Slog.w(TAG, "getAuthToken called with null account");
2640 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
2641 return;
2642 }
2643 if (authTokenType == null) {
2644 Slog.w(TAG, "getAuthToken called with null authTokenType");
2645 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
2646 return;
2647 }
2648 } catch (RemoteException e) {
2649 Slog.w(TAG, "Failed to report error back to the client." + e);
2650 return;
2651 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002652 int userId = UserHandle.getCallingUserId();
2653 long ident = Binder.clearCallingIdentity();
2654 final UserAccounts accounts;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002655 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002656 try {
2657 accounts = getUserAccounts(userId);
2658 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2659 AuthenticatorDescription.newKey(account.type), accounts.userId);
2660 } finally {
2661 Binder.restoreCallingIdentity(ident);
2662 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002663
Costin Manolachea40c6302010-12-13 14:50:45 -08002664 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07002665 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08002666
2667 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002668 final int callerUid = Binder.getCallingUid();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002669 final boolean permissionGranted =
2670 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08002671
Carlos Valdivia91979be2015-05-22 14:11:35 -07002672 // Get the calling package. We will use it for the purpose of caching.
2673 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07002674 List<String> callerOwnedPackageNames;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002675 ident = Binder.clearCallingIdentity();
Amith Yamasanie7360012015-06-03 17:39:40 -07002676 try {
2677 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
2678 } finally {
2679 Binder.restoreCallingIdentity(ident);
2680 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002681 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
2682 String msg = String.format(
2683 "Uid %s is attempting to illegally masquerade as package %s!",
2684 callerUid,
2685 callerPkg);
2686 throw new SecurityException(msg);
2687 }
2688
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002689 // let authenticator know the identity of the caller
2690 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
2691 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07002692
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002693 if (notifyOnAuthFailure) {
2694 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08002695 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002696
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002697 long identityToken = clearCallingIdentity();
2698 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07002699 // Distill the caller's package signatures into a single digest.
2700 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
2701
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002702 // if the caller has permission, do the peek. otherwise go the more expensive
2703 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08002704 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002705 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002706 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002707 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002708 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
2709 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2710 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002711 onResult(response, result);
2712 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002713 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002714 }
2715
Carlos Valdivia91979be2015-05-22 14:11:35 -07002716 if (customTokens) {
2717 /*
2718 * Look up tokens in the new cache only if the loginOptions don't have parameters
2719 * outside of those expected to be injected by the AccountManager, e.g.
2720 * ANDORID_PACKAGE_NAME.
2721 */
2722 String token = readCachedTokenInternal(
2723 accounts,
2724 account,
2725 authTokenType,
2726 callerPkg,
2727 callerPkgSigDigest);
2728 if (token != null) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002729 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2730 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2731 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002732 Bundle result = new Bundle();
2733 result.putString(AccountManager.KEY_AUTHTOKEN, token);
2734 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2735 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2736 onResult(response, result);
2737 return;
2738 }
2739 }
2740
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002741 new Session(
2742 accounts,
2743 response,
2744 account.type,
2745 expectActivityLaunch,
2746 false /* stripAuthTokenFromResult */,
2747 account.name,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002748 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002749 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002750 protected String toDebugString(long now) {
2751 if (loginOptions != null) loginOptions.keySet();
2752 return super.toDebugString(now) + ", getAuthToken"
2753 + ", " + account
2754 + ", authTokenType " + authTokenType
2755 + ", loginOptions " + loginOptions
2756 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2757 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002758
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002759 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002760 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002761 // If the caller doesn't have permission then create and return the
2762 // "grant permission" intent instead of the "getAuthToken" intent.
2763 if (!permissionGranted) {
2764 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2765 } else {
2766 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2767 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002768 }
2769
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002770 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002771 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002772 Bundle.setDefusable(result, true);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002773 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002774 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002775 Intent intent = newGrantCredentialsPermissionIntent(
2776 account,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002777 null,
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002778 callerUid,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002779 new AccountAuthenticatorResponse(this),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002780 authTokenType,
2781 true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002782 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002783 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002784 onResult(bundle);
2785 return;
2786 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002787 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002788 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002789 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2790 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002791 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002792 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002793 "the type and name should not be empty");
2794 return;
2795 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002796 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08002797 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002798 saveAuthTokenToDatabase(
2799 mAccounts,
2800 resultAccount,
2801 authTokenType,
2802 authToken);
2803 }
2804 long expiryMillis = result.getLong(
2805 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2806 if (customTokens
2807 && expiryMillis > System.currentTimeMillis()) {
2808 saveCachedToken(
2809 mAccounts,
2810 account,
2811 callerPkg,
2812 callerPkgSigDigest,
2813 authTokenType,
2814 authToken,
2815 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08002816 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002817 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002818
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002819 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08002820 if (intent != null && notifyOnAuthFailure && !customTokens) {
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002821 /*
2822 * Make sure that the supplied intent is owned by the authenticator
2823 * giving it to the system. Otherwise a malicious authenticator could
2824 * have users launching arbitrary activities by tricking users to
2825 * interact with malicious notifications.
2826 */
2827 checkKeyIntent(
2828 Binder.getCallingUid(),
2829 intent);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002830 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002831 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002832 intent, "android", accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002833 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002834 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002835 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07002836 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002837 }.bind();
2838 } finally {
2839 restoreCallingIdentity(identityToken);
2840 }
Fred Quintana60307342009-03-24 22:48:12 -07002841 }
2842
Carlos Valdivia91979be2015-05-22 14:11:35 -07002843 private byte[] calculatePackageSignatureDigest(String callerPkg) {
2844 MessageDigest digester;
2845 try {
2846 digester = MessageDigest.getInstance("SHA-256");
2847 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2848 callerPkg, PackageManager.GET_SIGNATURES);
2849 for (Signature sig : pkgInfo.signatures) {
2850 digester.update(sig.toByteArray());
2851 }
2852 } catch (NoSuchAlgorithmException x) {
2853 Log.wtf(TAG, "SHA-256 should be available", x);
2854 digester = null;
2855 } catch (NameNotFoundException e) {
2856 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2857 digester = null;
2858 }
2859 return (digester == null) ? null : digester.digest();
2860 }
2861
Dianne Hackborn41203752012-08-31 14:05:51 -07002862 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002863 String packageName, int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002864 int uid = intent.getIntExtra(
2865 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2866 String authTokenType = intent.getStringExtra(
2867 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
Eric Fischeree452ee2009-08-31 17:58:06 -07002868 final String titleAndSubtitle =
2869 mContext.getString(R.string.permission_request_notification_with_subtitle,
2870 account.name);
2871 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07002872 String title = titleAndSubtitle;
2873 String subtitle = "";
2874 if (index > 0) {
2875 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002876 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07002877 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002878 UserHandle user = UserHandle.of(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01002879 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04002880 Notification n = new Notification.Builder(contextForUser)
2881 .setSmallIcon(android.R.drawable.stat_sys_warning)
2882 .setWhen(0)
2883 .setColor(contextForUser.getColor(
2884 com.android.internal.R.color.system_notification_accent_color))
2885 .setContentTitle(title)
2886 .setContentText(subtitle)
2887 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2888 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2889 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002890 installNotification(getCredentialPermissionNotificationId(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002891 account, authTokenType, uid), n, packageName, user.getIdentifier());
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002892 }
2893
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002894 private Intent newGrantCredentialsPermissionIntent(Account account, String packageName,
2895 int uid, AccountAuthenticatorResponse response, String authTokenType,
2896 boolean startInNewTask) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002897
2898 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002899
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002900 if (startInNewTask) {
2901 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
2902 // Since it was set in Eclair+ we can't change it without breaking apps using
2903 // the intent from a non-Activity context. This is the default behavior.
2904 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2905 }
2906 intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account,
2907 authTokenType, uid) + (packageName != null ? packageName : "")));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002908 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002909 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2910 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002911 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002912
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002913 return intent;
2914 }
2915
2916 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2917 int uid) {
2918 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002919 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002920 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002921 final Pair<Pair<Account, String>, Integer> key =
2922 new Pair<Pair<Account, String>, Integer>(
2923 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002924 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002925 if (id == null) {
2926 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002927 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002928 }
2929 }
2930 return id;
2931 }
2932
Amith Yamasani04e0d262012-02-14 11:50:53 -08002933 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002934 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002935 synchronized (accounts.signinRequiredNotificationIds) {
2936 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002937 if (id == null) {
2938 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002939 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002940 }
2941 }
2942 return id;
2943 }
2944
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002945 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07002946 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07002947 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002948 final boolean expectActivityLaunch, final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002949 Bundle.setDefusable(optionsIn, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002950 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2951 Log.v(TAG, "addAccount: accountType " + accountType
2952 + ", response " + response
2953 + ", authTokenType " + authTokenType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002954 + ", requiredFeatures " + Arrays.toString(requiredFeatures)
Fred Quintana56285a62010-12-02 14:20:51 -08002955 + ", expectActivityLaunch " + expectActivityLaunch
2956 + ", caller's uid " + Binder.getCallingUid()
2957 + ", pid " + Binder.getCallingPid());
2958 }
Fred Quintana382601f2010-03-25 12:25:10 -07002959 if (response == null) throw new IllegalArgumentException("response is null");
2960 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002961
Amith Yamasani71e6c692013-03-24 17:39:28 -07002962 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002963 final int uid = Binder.getCallingUid();
2964 final int userId = UserHandle.getUserId(uid);
2965 if (!canUserModifyAccounts(userId, uid)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002966 try {
2967 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2968 "User is not allowed to add an account!");
2969 } catch (RemoteException re) {
2970 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002971 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002972 return;
2973 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002974 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002975 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002976 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2977 "User cannot modify accounts of this type (policy).");
2978 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002979 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002980 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2981 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002982 return;
2983 }
2984
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002985 final int pid = Binder.getCallingPid();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002986 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2987 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2988 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2989
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002990 int usrId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002991 long identityToken = clearCallingIdentity();
2992 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002993 UserAccounts accounts = getUserAccounts(usrId);
2994 logRecordWithUid(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07002995 accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
2996 uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002997 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002998 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002999 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003000 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003001 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07003002 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07003003 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003004 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003005
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003006 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003007 protected String toDebugString(long now) {
3008 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07003009 + ", accountType " + accountType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003010 + ", requiredFeatures " + Arrays.toString(requiredFeatures);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003011 }
3012 }.bind();
3013 } finally {
3014 restoreCallingIdentity(identityToken);
3015 }
Fred Quintana60307342009-03-24 22:48:12 -07003016 }
3017
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003018 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003019 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
3020 final String authTokenType, final String[] requiredFeatures,
3021 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003022 Bundle.setDefusable(optionsIn, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003023 int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003024 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3025 Log.v(TAG, "addAccount: accountType " + accountType
3026 + ", response " + response
3027 + ", authTokenType " + authTokenType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003028 + ", requiredFeatures " + Arrays.toString(requiredFeatures)
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003029 + ", expectActivityLaunch " + expectActivityLaunch
3030 + ", caller's uid " + Binder.getCallingUid()
3031 + ", pid " + Binder.getCallingPid()
3032 + ", for user id " + userId);
3033 }
3034 if (response == null) throw new IllegalArgumentException("response is null");
3035 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003036 // Only allow the system process to add accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003037 if (isCrossUser(callingUid, userId)) {
3038 throw new SecurityException(
3039 String.format(
3040 "User %s trying to add account for %s" ,
3041 UserHandle.getCallingUserId(),
3042 userId));
3043 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003044
3045 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003046 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003047 try {
3048 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3049 "User is not allowed to add an account!");
3050 } catch (RemoteException re) {
3051 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003052 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003053 return;
3054 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003055 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003056 try {
3057 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3058 "User cannot modify accounts of this type (policy).");
3059 } catch (RemoteException re) {
3060 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003061 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3062 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003063 return;
3064 }
3065
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003066 final int pid = Binder.getCallingPid();
3067 final int uid = Binder.getCallingUid();
3068 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3069 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3070 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3071
3072 long identityToken = clearCallingIdentity();
3073 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003074 UserAccounts accounts = getUserAccounts(userId);
3075 logRecordWithUid(
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003076 accounts, AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
3077 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003078 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003079 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003080 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003081 @Override
3082 public void run() throws RemoteException {
3083 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
3084 options);
3085 }
3086
3087 @Override
3088 protected String toDebugString(long now) {
3089 return super.toDebugString(now) + ", addAccount"
3090 + ", accountType " + accountType
3091 + ", requiredFeatures "
3092 + (requiredFeatures != null
3093 ? TextUtils.join(",", requiredFeatures)
3094 : null);
3095 }
3096 }.bind();
3097 } finally {
3098 restoreCallingIdentity(identityToken);
3099 }
3100 }
3101
Sandra Kwan78812282015-11-04 11:19:47 -08003102 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003103 public void startAddAccountSession(
3104 final IAccountManagerResponse response,
3105 final String accountType,
3106 final String authTokenType,
3107 final String[] requiredFeatures,
Sandra Kwan78812282015-11-04 11:19:47 -08003108 final boolean expectActivityLaunch,
3109 final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003110 Bundle.setDefusable(optionsIn, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003111 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3112 Log.v(TAG,
3113 "startAddAccountSession: accountType " + accountType
3114 + ", response " + response
3115 + ", authTokenType " + authTokenType
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003116 + ", requiredFeatures " + Arrays.toString(requiredFeatures)
Sandra Kwan78812282015-11-04 11:19:47 -08003117 + ", expectActivityLaunch " + expectActivityLaunch
3118 + ", caller's uid " + Binder.getCallingUid()
3119 + ", pid " + Binder.getCallingPid());
3120 }
3121 if (response == null) {
3122 throw new IllegalArgumentException("response is null");
3123 }
3124 if (accountType == null) {
3125 throw new IllegalArgumentException("accountType is null");
3126 }
3127
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003128 final int uid = Binder.getCallingUid();
3129 final int userId = UserHandle.getUserId(uid);
3130 if (!canUserModifyAccounts(userId, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003131 try {
3132 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3133 "User is not allowed to add an account!");
3134 } catch (RemoteException re) {
3135 }
3136 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3137 return;
3138 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003139 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003140 try {
3141 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3142 "User cannot modify accounts of this type (policy).");
3143 } catch (RemoteException re) {
3144 }
3145 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3146 userId);
3147 return;
3148 }
Sandra Kwan78812282015-11-04 11:19:47 -08003149 final int pid = Binder.getCallingPid();
Sandra Kwan78812282015-11-04 11:19:47 -08003150 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3151 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3152 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3153
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003154 // Check to see if the Password should be included to the caller.
3155 String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3156 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003157 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003158
Sandra Kwan78812282015-11-04 11:19:47 -08003159 long identityToken = clearCallingIdentity();
3160 try {
Hongming Jin368aa192016-07-29 14:29:54 -07003161 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003162 logRecordWithUid(accounts, AccountsDb.DEBUG_ACTION_CALLED_START_ACCOUNT_ADD,
3163 AccountsDb.TABLE_ACCOUNTS, uid);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003164 new StartAccountSession(
3165 accounts,
3166 response,
3167 accountType,
3168 expectActivityLaunch,
3169 null /* accountName */,
3170 false /* authDetailsRequired */,
3171 true /* updateLastAuthenticationTime */,
3172 isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003173 @Override
3174 public void run() throws RemoteException {
3175 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
3176 requiredFeatures, options);
3177 }
3178
3179 @Override
3180 protected String toDebugString(long now) {
3181 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
3182 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
3183 + accountType + ", requiredFeatures "
3184 + (requiredFeatures != null ? requiredFeaturesStr : null);
3185 }
3186 }.bind();
3187 } finally {
3188 restoreCallingIdentity(identityToken);
3189 }
3190 }
3191
3192 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
3193 private abstract class StartAccountSession extends Session {
3194
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003195 private final boolean mIsPasswordForwardingAllowed;
3196
3197 public StartAccountSession(
3198 UserAccounts accounts,
3199 IAccountManagerResponse response,
3200 String accountType,
3201 boolean expectActivityLaunch,
3202 String accountName,
3203 boolean authDetailsRequired,
3204 boolean updateLastAuthenticationTime,
3205 boolean isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003206 super(accounts, response, accountType, expectActivityLaunch,
3207 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
3208 updateLastAuthenticationTime);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003209 mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
Sandra Kwan78812282015-11-04 11:19:47 -08003210 }
3211
3212 @Override
3213 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003214 Bundle.setDefusable(result, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003215 mNumResults++;
3216 Intent intent = null;
Sandra Kwan78812282015-11-04 11:19:47 -08003217 if (result != null
3218 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08003219 checkKeyIntent(
3220 Binder.getCallingUid(),
3221 intent);
Sandra Kwan78812282015-11-04 11:19:47 -08003222 }
Sandra Kwan78812282015-11-04 11:19:47 -08003223 IAccountManagerResponse response;
3224 if (mExpectActivityLaunch && result != null
3225 && result.containsKey(AccountManager.KEY_INTENT)) {
3226 response = mResponse;
3227 } else {
3228 response = getResponseAndClose();
3229 }
3230 if (response == null) {
3231 return;
3232 }
3233 if (result == null) {
3234 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3235 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
3236 + response);
3237 }
3238 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3239 "null bundle returned");
3240 return;
3241 }
3242
3243 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
3244 // All AccountManager error codes are greater
3245 // than 0
3246 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
3247 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3248 return;
3249 }
3250
Hongming Jin368aa192016-07-29 14:29:54 -07003251 // Omit passwords if the caller isn't permitted to see them.
3252 if (!mIsPasswordForwardingAllowed) {
3253 result.remove(AccountManager.KEY_PASSWORD);
3254 }
3255
Sandra Kwan78812282015-11-04 11:19:47 -08003256 // Strip auth token from result.
3257 result.remove(AccountManager.KEY_AUTHTOKEN);
3258
3259 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3260 Log.v(TAG,
3261 getClass().getSimpleName() + " calling onResult() on response " + response);
3262 }
3263
3264 // Get the session bundle created by authenticator. The
3265 // bundle contains data necessary for finishing the session
3266 // later. The session bundle will be encrypted here and
3267 // decrypted later when trying to finish the session.
3268 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
3269 if (sessionBundle != null) {
3270 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3271 if (TextUtils.isEmpty(accountType)
Andreas Gampe9b041742015-12-11 17:23:33 -08003272 || !mAccountType.equalsIgnoreCase(accountType)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003273 Log.w(TAG, "Account type in session bundle doesn't match request.");
3274 }
3275 // Add accountType info to session bundle. This will
3276 // override any value set by authenticator.
3277 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
3278
3279 // Encrypt session bundle before returning to caller.
3280 try {
3281 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3282 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
3283 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
3284 } catch (GeneralSecurityException e) {
3285 if (Log.isLoggable(TAG, Log.DEBUG)) {
3286 Log.v(TAG, "Failed to encrypt session bundle!", e);
3287 }
3288 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3289 "failed to encrypt session bundle");
3290 return;
3291 }
3292 }
3293
3294 sendResponse(response, result);
3295 }
3296 }
3297
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003298 @Override
Sandra Kwan0b84b452016-01-20 15:25:42 -08003299 public void finishSessionAsUser(IAccountManagerResponse response,
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003300 @NonNull Bundle sessionBundle,
3301 boolean expectActivityLaunch,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003302 Bundle appInfo,
3303 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003304 Bundle.setDefusable(sessionBundle, true);
Sandra Kwan0b84b452016-01-20 15:25:42 -08003305 int callingUid = Binder.getCallingUid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003306 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3307 Log.v(TAG,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003308 "finishSession: response "+ response
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003309 + ", expectActivityLaunch " + expectActivityLaunch
Sandra Kwan0b84b452016-01-20 15:25:42 -08003310 + ", caller's uid " + callingUid
3311 + ", caller's user id " + UserHandle.getCallingUserId()
3312 + ", pid " + Binder.getCallingPid()
3313 + ", for user id " + userId);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003314 }
3315 if (response == null) {
3316 throw new IllegalArgumentException("response is null");
3317 }
3318
3319 // Session bundle is the encrypted bundle of the original bundle created by authenticator.
3320 // Account type is added to it before encryption.
3321 if (sessionBundle == null || sessionBundle.size() == 0) {
3322 throw new IllegalArgumentException("sessionBundle is empty");
3323 }
3324
Sandra Kwan0b84b452016-01-20 15:25:42 -08003325 // Only allow the system process to finish session for other users
3326 if (isCrossUser(callingUid, userId)) {
3327 throw new SecurityException(
3328 String.format(
3329 "User %s trying to finish session for %s without cross user permission",
3330 UserHandle.getCallingUserId(),
3331 userId));
3332 }
3333
Sandra Kwan0b84b452016-01-20 15:25:42 -08003334 if (!canUserModifyAccounts(userId, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003335 sendErrorResponse(response,
3336 AccountManager.ERROR_CODE_USER_RESTRICTED,
3337 "User is not allowed to add an account!");
3338 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3339 return;
3340 }
3341
3342 final int pid = Binder.getCallingPid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003343 final Bundle decryptedBundle;
3344 final String accountType;
3345 // First decrypt session bundle to get account type for checking permission.
3346 try {
3347 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3348 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
3349 if (decryptedBundle == null) {
3350 sendErrorResponse(
3351 response,
3352 AccountManager.ERROR_CODE_BAD_REQUEST,
3353 "failed to decrypt session bundle");
3354 return;
3355 }
3356 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3357 // Account type cannot be null. This should not happen if session bundle was created
3358 // properly by #StartAccountSession.
3359 if (TextUtils.isEmpty(accountType)) {
3360 sendErrorResponse(
3361 response,
3362 AccountManager.ERROR_CODE_BAD_ARGUMENTS,
3363 "accountType is empty");
3364 return;
3365 }
3366
3367 // If by any chances, decryptedBundle contains colliding keys with
3368 // system info
3369 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
3370 // update credentials flow, we should replace with the new values of the current call.
3371 if (appInfo != null) {
3372 decryptedBundle.putAll(appInfo);
3373 }
3374
3375 // Add info that may be used by add account or update credentials flow.
Sandra Kwan0b84b452016-01-20 15:25:42 -08003376 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003377 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
3378 } catch (GeneralSecurityException e) {
3379 if (Log.isLoggable(TAG, Log.DEBUG)) {
3380 Log.v(TAG, "Failed to decrypt session bundle!", e);
3381 }
3382 sendErrorResponse(
3383 response,
3384 AccountManager.ERROR_CODE_BAD_REQUEST,
3385 "failed to decrypt session bundle");
3386 return;
3387 }
3388
Sandra Kwan0b84b452016-01-20 15:25:42 -08003389 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003390 sendErrorResponse(
3391 response,
3392 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3393 "User cannot modify accounts of this type (policy).");
3394 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3395 userId);
3396 return;
3397 }
3398
3399 long identityToken = clearCallingIdentity();
3400 try {
3401 UserAccounts accounts = getUserAccounts(userId);
3402 logRecordWithUid(
3403 accounts,
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07003404 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_SESSION_FINISH,
3405 AccountsDb.TABLE_ACCOUNTS,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003406 callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003407 new Session(
3408 accounts,
3409 response,
3410 accountType,
3411 expectActivityLaunch,
3412 true /* stripAuthTokenFromResult */,
3413 null /* accountName */,
3414 false /* authDetailsRequired */,
3415 true /* updateLastAuthenticationTime */) {
3416 @Override
3417 public void run() throws RemoteException {
3418 mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
3419 }
3420
3421 @Override
3422 protected String toDebugString(long now) {
3423 return super.toDebugString(now)
3424 + ", finishSession"
3425 + ", accountType " + accountType;
3426 }
3427 }.bind();
3428 } finally {
3429 restoreCallingIdentity(identityToken);
3430 }
3431 }
3432
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003433 private void showCantAddAccount(int errorCode, int userId) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003434 final DevicePolicyManagerInternal dpmi =
3435 LocalServices.getService(DevicePolicyManagerInternal.class);
3436 Intent intent = null;
Nicolas Prevot14fc1972016-08-24 14:21:38 +01003437 if (dpmi == null) {
3438 intent = getDefaultCantAddAccountIntent(errorCode);
3439 } else if (errorCode == AccountManager.ERROR_CODE_USER_RESTRICTED) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003440 intent = dpmi.createUserRestrictionSupportIntent(userId,
3441 UserManager.DISALLOW_MODIFY_ACCOUNTS);
3442 } else if (errorCode == AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
3443 intent = dpmi.createShowAdminSupportIntent(userId, false);
3444 }
3445 if (intent == null) {
3446 intent = getDefaultCantAddAccountIntent(errorCode);
3447 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003448 long identityToken = clearCallingIdentity();
3449 try {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003450 mContext.startActivityAsUser(intent, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003451 } finally {
3452 restoreCallingIdentity(identityToken);
3453 }
3454 }
3455
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003456 /**
3457 * Called when we don't know precisely who is preventing us from adding an account.
3458 */
3459 private Intent getDefaultCantAddAccountIntent(int errorCode) {
3460 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
3461 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
3462 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3463 return cantAddAccount;
3464 }
3465
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003466 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003467 public void confirmCredentialsAsUser(
3468 IAccountManagerResponse response,
3469 final Account account,
3470 final Bundle options,
3471 final boolean expectActivityLaunch,
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003472 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003473 Bundle.setDefusable(options, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003474 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003475 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3476 Log.v(TAG, "confirmCredentials: " + account
3477 + ", response " + response
3478 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003479 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003480 + ", pid " + Binder.getCallingPid());
3481 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003482 // Only allow the system process to read accounts of other users
3483 if (isCrossUser(callingUid, userId)) {
3484 throw new SecurityException(
3485 String.format(
3486 "User %s trying to confirm account credentials for %s" ,
3487 UserHandle.getCallingUserId(),
3488 userId));
3489 }
Fred Quintana382601f2010-03-25 12:25:10 -07003490 if (response == null) throw new IllegalArgumentException("response is null");
3491 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003492 long identityToken = clearCallingIdentity();
3493 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003494 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003495 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003496 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003497 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003498 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003499 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003500 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003501 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003502 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003503 protected String toDebugString(long now) {
3504 return super.toDebugString(now) + ", confirmCredentials"
3505 + ", " + account;
3506 }
3507 }.bind();
3508 } finally {
3509 restoreCallingIdentity(identityToken);
3510 }
Fred Quintana60307342009-03-24 22:48:12 -07003511 }
3512
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003513 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003514 public void updateCredentials(IAccountManagerResponse response, final Account account,
3515 final String authTokenType, final boolean expectActivityLaunch,
3516 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003517 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003518 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3519 Log.v(TAG, "updateCredentials: " + account
3520 + ", response " + response
3521 + ", authTokenType " + authTokenType
3522 + ", expectActivityLaunch " + expectActivityLaunch
3523 + ", caller's uid " + Binder.getCallingUid()
3524 + ", pid " + Binder.getCallingPid());
3525 }
Fred Quintana382601f2010-03-25 12:25:10 -07003526 if (response == null) throw new IllegalArgumentException("response is null");
3527 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003528 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003529 long identityToken = clearCallingIdentity();
3530 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003531 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003532 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003533 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003534 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003535 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003536 public void run() throws RemoteException {
3537 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
3538 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003539 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003540 protected String toDebugString(long now) {
3541 if (loginOptions != null) loginOptions.keySet();
3542 return super.toDebugString(now) + ", updateCredentials"
3543 + ", " + account
3544 + ", authTokenType " + authTokenType
3545 + ", loginOptions " + loginOptions;
3546 }
3547 }.bind();
3548 } finally {
3549 restoreCallingIdentity(identityToken);
3550 }
Fred Quintana60307342009-03-24 22:48:12 -07003551 }
3552
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003553 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003554 public void startUpdateCredentialsSession(
3555 IAccountManagerResponse response,
3556 final Account account,
3557 final String authTokenType,
3558 final boolean expectActivityLaunch,
3559 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003560 Bundle.setDefusable(loginOptions, true);
Sandra Kwane68c37e2015-11-12 17:11:49 -08003561 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3562 Log.v(TAG,
3563 "startUpdateCredentialsSession: " + account + ", response " + response
3564 + ", authTokenType " + authTokenType + ", expectActivityLaunch "
3565 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
3566 + ", pid " + Binder.getCallingPid());
3567 }
3568 if (response == null) {
3569 throw new IllegalArgumentException("response is null");
3570 }
3571 if (account == null) {
3572 throw new IllegalArgumentException("account is null");
3573 }
Sandra Kwana578d112015-12-16 16:01:43 -08003574
3575 final int uid = Binder.getCallingUid();
Sandra Kwane68c37e2015-11-12 17:11:49 -08003576 int userId = UserHandle.getCallingUserId();
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003577
3578 // Check to see if the Password should be included to the caller.
3579 String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3580 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003581 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003582
Sandra Kwane68c37e2015-11-12 17:11:49 -08003583 long identityToken = clearCallingIdentity();
3584 try {
3585 UserAccounts accounts = getUserAccounts(userId);
3586 new StartAccountSession(
3587 accounts,
3588 response,
3589 account.type,
3590 expectActivityLaunch,
3591 account.name,
3592 false /* authDetailsRequired */,
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003593 true /* updateLastCredentialTime */,
3594 isPasswordForwardingAllowed) {
Sandra Kwane68c37e2015-11-12 17:11:49 -08003595 @Override
3596 public void run() throws RemoteException {
3597 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
3598 loginOptions);
3599 }
3600
3601 @Override
3602 protected String toDebugString(long now) {
3603 if (loginOptions != null)
3604 loginOptions.keySet();
3605 return super.toDebugString(now)
3606 + ", startUpdateCredentialsSession"
3607 + ", " + account
3608 + ", authTokenType " + authTokenType
3609 + ", loginOptions " + loginOptions;
3610 }
3611 }.bind();
3612 } finally {
3613 restoreCallingIdentity(identityToken);
3614 }
3615 }
3616
3617 @Override
Sandra Kwan390c9d22016-01-12 14:13:37 -08003618 public void isCredentialsUpdateSuggested(
3619 IAccountManagerResponse response,
3620 final Account account,
3621 final String statusToken) {
3622 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3623 Log.v(TAG,
3624 "isCredentialsUpdateSuggested: " + account + ", response " + response
3625 + ", caller's uid " + Binder.getCallingUid()
3626 + ", pid " + Binder.getCallingPid());
3627 }
3628 if (response == null) {
3629 throw new IllegalArgumentException("response is null");
3630 }
3631 if (account == null) {
3632 throw new IllegalArgumentException("account is null");
3633 }
3634 if (TextUtils.isEmpty(statusToken)) {
3635 throw new IllegalArgumentException("status token is empty");
3636 }
3637
Sandra Kwan390c9d22016-01-12 14:13:37 -08003638 int usrId = UserHandle.getCallingUserId();
3639 long identityToken = clearCallingIdentity();
3640 try {
3641 UserAccounts accounts = getUserAccounts(usrId);
3642 new Session(accounts, response, account.type, false /* expectActivityLaunch */,
3643 false /* stripAuthTokenFromResult */, account.name,
3644 false /* authDetailsRequired */) {
3645 @Override
3646 protected String toDebugString(long now) {
3647 return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
3648 + ", " + account;
3649 }
3650
3651 @Override
3652 public void run() throws RemoteException {
3653 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
3654 }
3655
3656 @Override
3657 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003658 Bundle.setDefusable(result, true);
Sandra Kwan390c9d22016-01-12 14:13:37 -08003659 IAccountManagerResponse response = getResponseAndClose();
3660 if (response == null) {
3661 return;
3662 }
3663
3664 if (result == null) {
3665 sendErrorResponse(
3666 response,
3667 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3668 "null bundle");
3669 return;
3670 }
3671
3672 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3673 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3674 + response);
3675 }
3676 // Check to see if an error occurred. We know if an error occurred because all
3677 // error codes are greater than 0.
3678 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
3679 sendErrorResponse(response,
3680 result.getInt(AccountManager.KEY_ERROR_CODE),
3681 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3682 return;
3683 }
3684 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
3685 sendErrorResponse(
3686 response,
3687 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3688 "no result in response");
3689 return;
3690 }
3691 final Bundle newResult = new Bundle();
3692 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
3693 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
3694 sendResponse(response, newResult);
3695 }
3696 }.bind();
3697 } finally {
3698 restoreCallingIdentity(identityToken);
3699 }
3700 }
3701
3702 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003703 public void editProperties(IAccountManagerResponse response, final String accountType,
3704 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003705 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003706 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3707 Log.v(TAG, "editProperties: accountType " + accountType
3708 + ", response " + response
3709 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003710 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003711 + ", pid " + Binder.getCallingPid());
3712 }
Fred Quintana382601f2010-03-25 12:25:10 -07003713 if (response == null) throw new IllegalArgumentException("response is null");
3714 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003715 int userId = UserHandle.getCallingUserId();
3716 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003717 String msg = String.format(
3718 "uid %s cannot edit authenticator properites for account type: %s",
3719 callingUid,
3720 accountType);
3721 throw new SecurityException(msg);
3722 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003723 long identityToken = clearCallingIdentity();
3724 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003725 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003726 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003727 true /* stripAuthTokenFromResult */, null /* accountName */,
3728 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003729 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003730 public void run() throws RemoteException {
3731 mAuthenticator.editProperties(this, mAccountType);
3732 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003733 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003734 protected String toDebugString(long now) {
3735 return super.toDebugString(now) + ", editProperties"
3736 + ", accountType " + accountType;
3737 }
3738 }.bind();
3739 } finally {
3740 restoreCallingIdentity(identityToken);
3741 }
Fred Quintana60307342009-03-24 22:48:12 -07003742 }
3743
Amith Yamasani12747872015-12-07 14:19:49 -08003744 @Override
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003745 public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName,
3746 @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003747 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003748 throw new SecurityException("Can be called only by system UID");
3749 }
3750 Preconditions.checkNotNull(account, "account cannot be null");
3751 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3752 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3753
3754 final int userId = userHandle.getIdentifier();
3755
3756 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3757
3758 try {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003759 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
Svet Ganovf6d424f12016-09-20 20:18:53 -07003760 return hasAccountAccess(account, packageName, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003761 } catch (NameNotFoundException e) {
3762 return false;
3763 }
3764 }
3765
Svet Ganovf6d424f12016-09-20 20:18:53 -07003766 private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
3767 int uid) {
3768 if (packageName == null) {
3769 String[] packageNames = mPackageManager.getPackagesForUid(uid);
3770 if (ArrayUtils.isEmpty(packageNames)) {
3771 return false;
3772 }
3773 // For app op checks related to permissions all packages in the UID
3774 // have the same app op state, so doesn't matter which one we pick.
3775 packageName = packageNames[0];
3776 }
3777
3778 // Use null token which means any token. Having a token means the package
3779 // is trusted by the authenticator, hence it is fine to access the account.
3780 if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) {
3781 return true;
3782 }
3783 // In addition to the permissions required to get an auth token we also allow
3784 // the account to be accessed by holders of the get accounts permissions.
3785 return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
3786 || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
3787 }
3788
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003789 private boolean checkUidPermission(String permission, int uid, String opPackageName) {
3790 final long identity = Binder.clearCallingIdentity();
3791 try {
3792 IPackageManager pm = ActivityThread.getPackageManager();
3793 if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
3794 return false;
3795 }
3796 final int opCode = AppOpsManager.permissionToOpCode(permission);
3797 return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
3798 opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
3799 } catch (RemoteException e) {
3800 /* ignore - local call */
3801 } finally {
3802 Binder.restoreCallingIdentity(identity);
3803 }
3804 return false;
3805 }
3806
3807 @Override
3808 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
3809 @NonNull String packageName, @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003810 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003811 throw new SecurityException("Can be called only by system UID");
3812 }
3813
3814 Preconditions.checkNotNull(account, "account cannot be null");
3815 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3816 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3817
3818 final int userId = userHandle.getIdentifier();
3819
3820 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3821
3822 final int uid;
3823 try {
3824 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3825 } catch (NameNotFoundException e) {
3826 Slog.e(TAG, "Unknown package " + packageName);
3827 return null;
3828 }
3829
3830 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
3831
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003832 final long identity = Binder.clearCallingIdentity();
3833 try {
3834 return PendingIntent.getActivityAsUser(
3835 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
3836 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3837 null, new UserHandle(userId)).getIntentSender();
3838 } finally {
3839 Binder.restoreCallingIdentity(identity);
3840 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003841 }
3842
3843 private Intent newRequestAccountAccessIntent(Account account, String packageName,
3844 int uid, RemoteCallback callback) {
3845 return newGrantCredentialsPermissionIntent(account, packageName, uid,
3846 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
3847 @Override
3848 public void onResult(Bundle value) throws RemoteException {
3849 handleAuthenticatorResponse(true);
3850 }
3851
3852 @Override
3853 public void onRequestContinued() {
3854 /* ignore */
3855 }
3856
3857 @Override
3858 public void onError(int errorCode, String errorMessage) throws RemoteException {
3859 handleAuthenticatorResponse(false);
3860 }
3861
3862 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
3863 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -07003864 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003865 UserHandle.getUserHandleForUid(uid));
3866 if (callback != null) {
3867 Bundle result = new Bundle();
3868 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
3869 callback.sendResult(result);
3870 }
3871 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07003872 }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003873 }
3874
3875 @Override
Amith Yamasani12747872015-12-07 14:19:49 -08003876 public boolean someUserHasAccount(@NonNull final Account account) {
3877 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
3878 throw new SecurityException("Only system can check for accounts across users");
3879 }
3880 final long token = Binder.clearCallingIdentity();
3881 try {
3882 AccountAndUser[] allAccounts = getAllAccounts();
3883 for (int i = allAccounts.length - 1; i >= 0; i--) {
3884 if (allAccounts[i].account.equals(account)) {
3885 return true;
3886 }
3887 }
3888 return false;
3889 } finally {
3890 Binder.restoreCallingIdentity(token);
3891 }
3892 }
3893
Fred Quintana33269202009-04-20 16:05:10 -07003894 private class GetAccountsByTypeAndFeatureSession extends Session {
3895 private final String[] mFeatures;
3896 private volatile Account[] mAccountsOfType = null;
3897 private volatile ArrayList<Account> mAccountsWithFeatures = null;
3898 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003899 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07003900
Amith Yamasani04e0d262012-02-14 11:50:53 -08003901 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003902 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003903 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003904 true /* stripAuthTokenFromResult */, null /* accountName */,
3905 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003906 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07003907 mFeatures = features;
3908 }
3909
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003910 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003911 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08003912 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003913 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
3914 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003915 }
Fred Quintana33269202009-04-20 16:05:10 -07003916 // check whether each account matches the requested features
Tejas Khorana5edff3b2016-06-28 20:59:52 -07003917 mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
Fred Quintana33269202009-04-20 16:05:10 -07003918 mCurrentAccount = 0;
3919
3920 checkAccount();
3921 }
3922
3923 public void checkAccount() {
3924 if (mCurrentAccount >= mAccountsOfType.length) {
3925 sendResult();
3926 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07003927 }
Fred Quintana33269202009-04-20 16:05:10 -07003928
Fred Quintana29e94b82010-03-10 12:11:51 -08003929 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
3930 if (accountAuthenticator == null) {
3931 // It is possible that the authenticator has died, which is indicated by
3932 // mAuthenticator being set to null. If this happens then just abort.
3933 // There is no need to send back a result or error in this case since
3934 // that already happened when mAuthenticator was cleared.
3935 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3936 Log.v(TAG, "checkAccount: aborting session since we are no longer"
3937 + " connected to the authenticator, " + toDebugString());
3938 }
3939 return;
3940 }
Fred Quintana33269202009-04-20 16:05:10 -07003941 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08003942 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07003943 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003944 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07003945 }
3946 }
3947
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003948 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003949 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003950 Bundle.setDefusable(result, true);
Fred Quintana33269202009-04-20 16:05:10 -07003951 mNumResults++;
3952 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003953 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07003954 return;
3955 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003956 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07003957 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3958 }
3959 mCurrentAccount++;
3960 checkAccount();
3961 }
3962
3963 public void sendResult() {
3964 IAccountManagerResponse response = getResponseAndClose();
3965 if (response != null) {
3966 try {
3967 Account[] accounts = new Account[mAccountsWithFeatures.size()];
3968 for (int i = 0; i < accounts.length; i++) {
3969 accounts[i] = mAccountsWithFeatures.get(i);
3970 }
Fred Quintana56285a62010-12-02 14:20:51 -08003971 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3972 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3973 + response);
3974 }
Fred Quintana33269202009-04-20 16:05:10 -07003975 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003976 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07003977 response.onResult(result);
3978 } catch (RemoteException e) {
3979 // if the caller is dead then there is no one to care about remote exceptions
3980 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3981 Log.v(TAG, "failure while notifying response", e);
3982 }
3983 }
3984 }
3985 }
3986
3987
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003988 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003989 protected String toDebugString(long now) {
3990 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3991 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3992 }
3993 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003994
Amith Yamasani04e0d262012-02-14 11:50:53 -08003995 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003996 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08003997 * @hide
3998 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003999 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004000 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004001 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07004002 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4003 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004004 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004005 return new Account[0];
4006 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004007 long identityToken = clearCallingIdentity();
4008 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004009 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004010 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004011 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004012 callingUid,
4013 null, // packageName
4014 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004015 } finally {
4016 restoreCallingIdentity(identityToken);
4017 }
4018 }
4019
Amith Yamasanif29f2362012-04-05 18:29:52 -07004020 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004021 * Returns accounts for all running users.
4022 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07004023 * @hide
4024 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004025 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004026 public AccountAndUser[] getRunningAccounts() {
4027 final int[] runningUserIds;
4028 try {
4029 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
4030 } catch (RemoteException e) {
4031 // Running in system_server; should never happen
4032 throw new RuntimeException(e);
4033 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004034 return getAccounts(runningUserIds);
4035 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07004036
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004037 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004038 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004039 public AccountAndUser[] getAllAccounts() {
Amith Yamasanid04aaa32016-06-13 12:09:36 -07004040 final List<UserInfo> users = getUserManager().getUsers(true);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004041 final int[] userIds = new int[users.size()];
4042 for (int i = 0; i < userIds.length; i++) {
4043 userIds[i] = users.get(i).id;
4044 }
4045 return getAccounts(userIds);
4046 }
4047
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004048 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004049 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004050 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004051 for (int userId : userIds) {
4052 UserAccounts userAccounts = getUserAccounts(userId);
4053 if (userAccounts == null) continue;
4054 synchronized (userAccounts.cacheLock) {
4055 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
4056 Binder.getCallingUid(), null);
4057 for (int a = 0; a < accounts.length; a++) {
4058 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07004059 }
4060 }
4061 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004062
4063 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
4064 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07004065 }
4066
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004067 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004068 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004069 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
4070 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004071 }
4072
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004073 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004074 private Account[] getAccountsAsUser(
4075 String type,
4076 int userId,
4077 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004078 int packageUid,
4079 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004080 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004081 // Only allow the system process to read accounts of other users
4082 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07004083 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08004084 && mContext.checkCallingOrSelfPermission(
4085 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
4086 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004087 throw new SecurityException("User " + UserHandle.getCallingUserId()
4088 + " trying to get account for " + userId);
4089 }
4090
Fred Quintana56285a62010-12-02 14:20:51 -08004091 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4092 Log.v(TAG, "getAccounts: accountType " + type
4093 + ", caller's uid " + Binder.getCallingUid()
4094 + ", pid " + Binder.getCallingPid());
4095 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004096 // If the original calling app was using the framework account chooser activity, we'll
4097 // be passed in the original caller's uid here, which is what should be used for filtering.
4098 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
4099 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07004100 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07004101 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004102 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4103 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004104 if (visibleAccountTypes.isEmpty()
4105 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004106 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004107 } else if (visibleAccountTypes.contains(type)) {
4108 // Prune the list down to just the requested type.
4109 visibleAccountTypes = new ArrayList<>();
4110 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004111 } // else aggregate all the visible accounts (it won't matter if the
4112 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004113
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004114 long identityToken = clearCallingIdentity();
4115 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004116 UserAccounts accounts = getUserAccounts(userId);
Tejas Khorana69990d92016-08-03 11:19:40 -07004117 Account[] accountsToReturn = getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004118 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004119 callingUid,
4120 callingPackage,
4121 visibleAccountTypes);
Tejas Khorana69990d92016-08-03 11:19:40 -07004122 ArrayList<Account> accountsToReturnList = new
4123 ArrayList<Account>(Arrays.asList(accountsToReturn));
4124 for(int i = accountsToReturnList.size() - 1; i >= 0 ; i--) {
4125 // if account not visible to caller or managed by caller, remove from
4126 // accounts to return. Note that all accounts visible by default unless
4127 // visible list functionality implemented
4128 if(!(isAccountVisible(accountsToReturnList.get(i), callingUid,
4129 getUserAccounts(userId)))) {
4130 accountsToReturnList.remove(i);
4131 }
4132 }
4133 return accountsToReturnList.toArray(new Account[accountsToReturnList.size()]);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004134 } finally {
4135 restoreCallingIdentity(identityToken);
4136 }
4137 }
4138
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004139 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004140 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004141 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004142 int callingUid,
4143 String callingPackage,
4144 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004145 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004146 ArrayList<Account> visibleAccounts = new ArrayList<>();
4147 for (String visibleType : visibleAccountTypes) {
4148 Account[] accountsForType = getAccountsFromCacheLocked(
4149 userAccounts, visibleType, callingUid, callingPackage);
4150 if (accountsForType != null) {
4151 visibleAccounts.addAll(Arrays.asList(accountsForType));
4152 }
4153 }
4154 Account[] result = new Account[visibleAccounts.size()];
4155 for (int i = 0; i < visibleAccounts.size(); i++) {
4156 result[i] = visibleAccounts.get(i);
4157 }
4158 return result;
4159 }
4160 }
4161
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004162 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004163 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07004164 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004165 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
4166 for (Account account : accounts) {
4167 addSharedAccountAsUser(account, userId);
4168 }
4169 }
4170
4171 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004172 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004173 UserAccounts accounts = getUserAccounts(userId);
4174 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004175 DeDatabaseHelper.deleteSharedAccount(db, account);
4176 long accountId = DeDatabaseHelper.insertSharedAccount(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004177 if (accountId < 0) {
4178 Log.w(TAG, "insertAccountIntoDatabase: " + account
4179 + ", skipping the DB insert failed");
4180 return false;
4181 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004182 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_SHARED_ACCOUNTS, accountId,
4183 accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004184 return true;
4185 }
4186
4187 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004188 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
4189 userId = handleIncomingUser(userId);
4190 UserAccounts accounts = getUserAccounts(userId);
4191 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004192 long sharedTableAccountId = DeDatabaseHelper.findSharedAccountId(db, account);
4193 int r = DeDatabaseHelper.renameSharedAccount(db, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004194 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004195 int callingUid = getCallingUid();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004196 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_RENAME, AccountsDb.TABLE_SHARED_ACCOUNTS,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004197 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004198 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004199 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004200 }
4201 return r > 0;
4202 }
4203
4204 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08004205 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004206 return removeSharedAccountAsUser(account, userId, getCallingUid());
4207 }
4208
4209 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004210 userId = handleIncomingUser(userId);
4211 UserAccounts accounts = getUserAccounts(userId);
4212 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004213 long sharedTableAccountId = DeDatabaseHelper.findSharedAccountId(db, account);
4214 boolean deleted = DeDatabaseHelper.deleteSharedAccount(db, account);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004215 if (deleted) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004216 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE, AccountsDb.TABLE_SHARED_ACCOUNTS,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004217 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07004218 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004219 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004220 return deleted;
Amith Yamasani67df64b2012-12-14 12:09:36 -08004221 }
4222
4223 @Override
4224 public Account[] getSharedAccountsAsUser(int userId) {
4225 userId = handleIncomingUser(userId);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004226 SQLiteDatabase db = getUserAccounts(userId).openHelper.getReadableDatabase();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004227 List<Account> accountList = DeDatabaseHelper.getSharedAccounts(db);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004228 Account[] accountArray = new Account[accountList.size()];
4229 accountList.toArray(accountArray);
4230 return accountArray;
4231 }
4232
4233 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004234 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004235 public Account[] getAccounts(String type, String opPackageName) {
Tejas Khorana69990d92016-08-03 11:19:40 -07004236 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004237 }
4238
Amith Yamasani27db4682013-03-30 17:07:47 -07004239 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004240 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004241 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004242 int callingUid = Binder.getCallingUid();
4243 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
4244 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
4245 + callingUid + " with uid=" + uid);
4246 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004247 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
4248 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004249 }
4250
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004251 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004252 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004253 public Account[] getAccountsByTypeForPackage(String type, String packageName,
4254 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004255 int packageUid = -1;
4256 try {
4257 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07004258 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
4259 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004260 } catch (RemoteException re) {
4261 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
4262 return new Account[0];
4263 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004264 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
4265 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004266 }
4267
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004268 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004269 public void getAccountsByFeatures(
4270 IAccountManagerResponse response,
4271 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004272 String[] features,
4273 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004274 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08004275 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4276 Log.v(TAG, "getAccounts: accountType " + type
4277 + ", response " + response
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004278 + ", features " + Arrays.toString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004279 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08004280 + ", pid " + Binder.getCallingPid());
4281 }
Fred Quintana382601f2010-03-25 12:25:10 -07004282 if (response == null) throw new IllegalArgumentException("response is null");
4283 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004284 int userId = UserHandle.getCallingUserId();
4285
Svetoslavf3f02ac2015-09-08 14:36:35 -07004286 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4287 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004288 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004289 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004290 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004291 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
4292 try {
4293 response.onResult(result);
4294 } catch (RemoteException e) {
4295 Log.e(TAG, "Cannot respond to caller do to exception." , e);
4296 }
4297 return;
4298 }
Fred Quintana33269202009-04-20 16:05:10 -07004299 long identityToken = clearCallingIdentity();
4300 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004301 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004302 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004303 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004304 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004305 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004306 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08004307 Bundle result = new Bundle();
4308 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4309 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004310 return;
4311 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004312 new GetAccountsByTypeAndFeatureSession(
4313 userAccounts,
4314 response,
4315 type,
4316 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004317 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07004318 } finally {
4319 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07004320 }
4321 }
4322
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07004323 @Override
4324 public void onAccountAccessed(String token) throws RemoteException {
4325 final int uid = Binder.getCallingUid();
4326 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
4327 return;
4328 }
4329 final int userId = UserHandle.getCallingUserId();
4330 final long identity = Binder.clearCallingIdentity();
4331 try {
4332 for (Account account : getAccounts(userId, mContext.getOpPackageName())) {
4333 if (Objects.equals(account.getAccessId(), token)) {
4334 // An app just accessed the account. At this point it knows about
4335 // it and there is not need to hide this account from the app.
4336 if (!hasAccountAccess(account, null, uid)) {
4337 updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
4338 uid, true);
4339 }
4340 }
4341 }
4342 } finally {
4343 Binder.restoreCallingIdentity(identity);
4344 }
4345 }
4346
Fred Quintanaa698f422009-04-08 19:14:54 -07004347 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07004348 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07004349 IAccountManagerResponse mResponse;
4350 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004351 final boolean mExpectActivityLaunch;
4352 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004353 final String mAccountName;
4354 // Indicates if we need to add auth details(like last credential time)
4355 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004356 // If set, we need to update the last authenticated time. This is
4357 // currently
4358 // used on
4359 // successful confirming credentials.
4360 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07004361
Fred Quintana33269202009-04-20 16:05:10 -07004362 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07004363 private int mNumRequestContinued = 0;
4364 private int mNumErrors = 0;
4365
Fred Quintana60307342009-03-24 22:48:12 -07004366 IAccountAuthenticator mAuthenticator = null;
4367
Fred Quintana8570f742010-02-18 10:32:54 -08004368 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004369 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004370
Amith Yamasani04e0d262012-02-14 11:50:53 -08004371 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004372 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4373 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004374 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4375 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4376 }
4377
4378 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4379 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4380 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07004381 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08004382 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07004383 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08004384 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004385 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07004386 mResponse = response;
4387 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004388 mExpectActivityLaunch = expectActivityLaunch;
4389 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004390 mAccountName = accountName;
4391 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004392 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004393
Fred Quintanaa698f422009-04-08 19:14:54 -07004394 synchronized (mSessions) {
4395 mSessions.put(toString(), this);
4396 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004397 if (response != null) {
4398 try {
4399 response.asBinder().linkToDeath(this, 0 /* flags */);
4400 } catch (RemoteException e) {
4401 mResponse = null;
4402 binderDied();
4403 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004404 }
Fred Quintana60307342009-03-24 22:48:12 -07004405 }
4406
Fred Quintanaa698f422009-04-08 19:14:54 -07004407 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07004408 if (mResponse == null) {
4409 // this session has already been closed
4410 return null;
4411 }
Fred Quintana60307342009-03-24 22:48:12 -07004412 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07004413 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07004414 return response;
4415 }
4416
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004417 /**
4418 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4419 * security policy.
4420 *
4421 * In particular we want to make sure that the Authenticator doesn't try to trick users
4422 * into launching aribtrary intents on the device via by tricking to click authenticator
4423 * supplied entries in the system Settings app.
4424 */
4425 protected void checkKeyIntent(
4426 int authUid,
4427 Intent intent) throws SecurityException {
4428 long bid = Binder.clearCallingIdentity();
4429 try {
4430 PackageManager pm = mContext.getPackageManager();
4431 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4432 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4433 int targetUid = targetActivityInfo.applicationInfo.uid;
Sandra Kwan0e961a12016-06-30 14:34:01 -07004434 if (!GrantCredentialsPermissionActivity.class.getName().equals(
4435 targetActivityInfo.getClass().getName())
4436 && !CantAddAccountActivity.class
4437 .equals(targetActivityInfo.getClass().getName())
4438 && PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
4439 targetUid)) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004440 String pkgName = targetActivityInfo.packageName;
4441 String activityName = targetActivityInfo.name;
4442 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4443 + "does not share a signature with the supplying authenticator (%s).";
4444 throw new SecurityException(
4445 String.format(tmpl, activityName, pkgName, mAccountType));
4446 }
4447 } finally {
4448 Binder.restoreCallingIdentity(bid);
4449 }
4450 }
4451
Fred Quintanaa698f422009-04-08 19:14:54 -07004452 private void close() {
4453 synchronized (mSessions) {
4454 if (mSessions.remove(toString()) == null) {
4455 // the session was already closed, so bail out now
4456 return;
4457 }
4458 }
4459 if (mResponse != null) {
4460 // stop listening for response deaths
4461 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4462
4463 // clear this so that we don't accidentally send any further results
4464 mResponse = null;
4465 }
4466 cancelTimeout();
4467 unbind();
4468 }
4469
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004470 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004471 public void binderDied() {
4472 mResponse = null;
4473 close();
4474 }
4475
4476 protected String toDebugString() {
4477 return toDebugString(SystemClock.elapsedRealtime());
4478 }
4479
4480 protected String toDebugString(long now) {
4481 return "Session: expectLaunch " + mExpectActivityLaunch
4482 + ", connected " + (mAuthenticator != null)
4483 + ", stats (" + mNumResults + "/" + mNumRequestContinued
4484 + "/" + mNumErrors + ")"
4485 + ", lifetime " + ((now - mCreationTime) / 1000.0);
4486 }
4487
Fred Quintana60307342009-03-24 22:48:12 -07004488 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004489 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4490 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4491 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004492 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004493 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004494 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07004495 }
4496 }
4497
4498 private void unbind() {
4499 if (mAuthenticator != null) {
4500 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07004501 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07004502 }
4503 }
4504
Fred Quintana60307342009-03-24 22:48:12 -07004505 public void cancelTimeout() {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004506 mHandler.removeMessages(MESSAGE_TIMED_OUT, this);
Fred Quintana60307342009-03-24 22:48:12 -07004507 }
4508
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004509 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004510 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07004511 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07004512 try {
4513 run();
4514 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004515 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07004516 "remote exception");
4517 }
Fred Quintana60307342009-03-24 22:48:12 -07004518 }
4519
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004520 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004521 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004522 mAuthenticator = null;
4523 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004524 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004525 try {
4526 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4527 "disconnected");
4528 } catch (RemoteException e) {
4529 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4530 Log.v(TAG, "Session.onServiceDisconnected: "
4531 + "caught RemoteException while responding", e);
4532 }
4533 }
Fred Quintana60307342009-03-24 22:48:12 -07004534 }
4535 }
4536
Fred Quintanab839afc2009-10-14 15:57:28 -07004537 public abstract void run() throws RemoteException;
4538
Fred Quintana60307342009-03-24 22:48:12 -07004539 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004540 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004541 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004542 try {
4543 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4544 "timeout");
4545 } catch (RemoteException e) {
4546 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4547 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4548 e);
4549 }
4550 }
Fred Quintana60307342009-03-24 22:48:12 -07004551 }
4552 }
4553
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004554 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004555 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004556 Bundle.setDefusable(result, true);
Fred Quintanaa698f422009-04-08 19:14:54 -07004557 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004558 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004559 if (result != null) {
4560 boolean isSuccessfulConfirmCreds = result.getBoolean(
4561 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004562 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004563 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4564 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004565 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004566 // mUpdateLastAuthenticatedTime is true and the confirmRequest
4567 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07004568 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004569 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004570 if (needUpdate || mAuthDetailsRequired) {
4571 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4572 if (needUpdate && accountPresent) {
4573 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4574 }
4575 if (mAuthDetailsRequired) {
4576 long lastAuthenticatedTime = -1;
4577 if (accountPresent) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004578 lastAuthenticatedTime = DeDatabaseHelper
4579 .findAccountLastAuthenticatedTime(
4580 mAccounts.openHelper.getReadableDatabase(),
4581 new Account(mAccountName, mAccountType));
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004582 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07004583 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004584 lastAuthenticatedTime);
4585 }
4586 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004587 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004588 if (result != null
4589 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004590 checkKeyIntent(
4591 Binder.getCallingUid(),
4592 intent);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004593 }
4594 if (result != null
4595 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004596 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4597 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004598 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4599 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004600 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4601 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004602 }
Fred Quintana60307342009-03-24 22:48:12 -07004603 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004604 IAccountManagerResponse response;
4605 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004606 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004607 response = mResponse;
4608 } else {
4609 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004610 }
Fred Quintana60307342009-03-24 22:48:12 -07004611 if (response != null) {
4612 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07004613 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08004614 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4615 Log.v(TAG, getClass().getSimpleName()
4616 + " calling onError() on response " + response);
4617 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004618 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07004619 "null bundle returned");
4620 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08004621 if (mStripAuthTokenFromResult) {
4622 result.remove(AccountManager.KEY_AUTHTOKEN);
4623 }
Fred Quintana56285a62010-12-02 14:20:51 -08004624 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4625 Log.v(TAG, getClass().getSimpleName()
4626 + " calling onResult() on response " + response);
4627 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004628 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4629 (intent == null)) {
4630 // All AccountManager error codes are greater than 0
4631 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4632 result.getString(AccountManager.KEY_ERROR_MESSAGE));
4633 } else {
4634 response.onResult(result);
4635 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004636 }
Fred Quintana60307342009-03-24 22:48:12 -07004637 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004638 // if the caller is dead then there is no one to care about remote exceptions
4639 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4640 Log.v(TAG, "failure while notifying response", e);
4641 }
Fred Quintana60307342009-03-24 22:48:12 -07004642 }
4643 }
4644 }
Fred Quintana60307342009-03-24 22:48:12 -07004645
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004646 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004647 public void onRequestContinued() {
4648 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07004649 }
4650
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004651 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004652 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004653 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07004654 IAccountManagerResponse response = getResponseAndClose();
4655 if (response != null) {
4656 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08004657 Log.v(TAG, getClass().getSimpleName()
4658 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07004659 }
4660 try {
4661 response.onError(errorCode, errorMessage);
4662 } catch (RemoteException e) {
4663 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4664 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4665 }
4666 }
4667 } else {
4668 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4669 Log.v(TAG, "Session.onError: already closed");
4670 }
Fred Quintana60307342009-03-24 22:48:12 -07004671 }
4672 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004673
4674 /**
4675 * find the component name for the authenticator and initiate a bind
4676 * if no authenticator or the bind fails then return false, otherwise return true
4677 */
4678 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004679 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4680 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4681 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07004682 if (authenticatorInfo == null) {
4683 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4684 Log.v(TAG, "there is no authenticator for " + authenticatorType
4685 + ", bailing out");
4686 }
4687 return false;
4688 }
4689
Jeff Sharkeyce18c812016-04-27 16:00:41 -06004690 if (!isLocalUnlockedUser(mAccounts.userId)
Jeff Sharkey8a372a02016-03-16 16:25:45 -06004691 && !authenticatorInfo.componentInfo.directBootAware) {
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07004692 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4693 + " which isn't encryption aware");
4694 return false;
4695 }
4696
Fred Quintanab839afc2009-10-14 15:57:28 -07004697 Intent intent = new Intent();
4698 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4699 intent.setComponent(authenticatorInfo.componentName);
4700 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4701 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4702 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08004703 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004704 UserHandle.of(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07004705 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4706 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4707 }
4708 return false;
4709 }
4710
Fred Quintanab839afc2009-10-14 15:57:28 -07004711 return true;
4712 }
Fred Quintana60307342009-03-24 22:48:12 -07004713 }
4714
Svet Ganov5d09c992016-09-07 09:57:41 -07004715 class MessageHandler extends Handler {
Fred Quintana60307342009-03-24 22:48:12 -07004716 MessageHandler(Looper looper) {
4717 super(looper);
4718 }
Costin Manolache3348f142009-09-29 18:58:36 -07004719
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004720 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004721 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07004722 switch (msg.what) {
4723 case MESSAGE_TIMED_OUT:
4724 Session session = (Session)msg.obj;
4725 session.onTimedOut();
4726 break;
4727
Amith Yamasani5be347b2013-03-31 17:44:31 -07004728 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00004729 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07004730 break;
4731
Fred Quintana60307342009-03-24 22:48:12 -07004732 default:
4733 throw new IllegalStateException("unhandled message: " + msg.what);
4734 }
4735 }
4736 }
4737
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004738 private void logRecord(UserAccounts accounts, String action, String tableName) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004739 logRecord(action, tableName, -1, accounts);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004740 }
4741
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004742 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004743 logRecord(action, tableName, -1, accounts, uid);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004744 }
4745
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004746 /*
4747 * This function receives an opened writable database.
4748 */
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004749 private void logRecord(String action, String tableName, long accountId,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004750 UserAccounts userAccount) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004751 logRecord(action, tableName, accountId, userAccount, getCallingUid());
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004752 }
4753
4754 /*
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004755 * This function receives an opened writable database and writes to it in a separate thread.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004756 */
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004757 private void logRecord(String action, String tableName, long accountId,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004758 UserAccounts userAccount, int callingUid) {
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004759
4760 class LogRecordTask implements Runnable {
4761 private final String action;
4762 private final String tableName;
4763 private final long accountId;
4764 private final UserAccounts userAccount;
4765 private final int callingUid;
4766 private final long userDebugDbInsertionPoint;
4767
4768 LogRecordTask(final String action,
4769 final String tableName,
4770 final long accountId,
4771 final UserAccounts userAccount,
4772 final int callingUid,
4773 final long userDebugDbInsertionPoint) {
4774 this.action = action;
4775 this.tableName = tableName;
4776 this.accountId = accountId;
4777 this.userAccount = userAccount;
4778 this.callingUid = callingUid;
4779 this.userDebugDbInsertionPoint = userDebugDbInsertionPoint;
4780 }
4781
4782 public void run() {
4783 SQLiteStatement logStatement = userAccount.statementForLogging;
4784 logStatement.bindLong(1, accountId);
4785 logStatement.bindString(2, action);
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004786 logStatement.bindString(3, mDateFormat.format(new Date()));
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004787 logStatement.bindLong(4, callingUid);
4788 logStatement.bindString(5, tableName);
4789 logStatement.bindLong(6, userDebugDbInsertionPoint);
4790 logStatement.execute();
4791 logStatement.clearBindings();
4792 }
4793 }
4794
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004795 LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
4796 callingUid, userAccount.debugDbInsertionPoint);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004797 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
4798 % MAX_DEBUG_DB_SIZE;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004799 mHandler.post(logTask);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004800 }
4801
4802 /*
4803 * This should only be called once to compile the sql statement for logging
4804 * and to find the insertion point.
4805 */
4806 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
4807 UserAccounts userAccount) {
4808 // Initialize the count if not done earlier.
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004809 int size = DebugDbHelper.getDebugTableRowCount(db);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004810 if (size >= MAX_DEBUG_DB_SIZE) {
4811 // Table is full, and we need to find the point where to insert.
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004812 userAccount.debugDbInsertionPoint = DebugDbHelper.getDebugTableInsertionPoint(db);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004813 } else {
4814 userAccount.debugDbInsertionPoint = size;
4815 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004816 userAccount.statementForLogging = DebugDbHelper.compileSqlStatementForLogging(db);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004817 }
4818
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004819 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07004820 return asBinder();
4821 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004822
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004823 /**
4824 * Searches array of arguments for the specified string
4825 * @param args array of argument strings
4826 * @param value value to search for
4827 * @return true if the value is contained in the array
4828 */
4829 private static boolean scanArgs(String[] args, String value) {
4830 if (args != null) {
4831 for (String arg : args) {
4832 if (value.equals(arg)) {
4833 return true;
4834 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004835 }
4836 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004837 return false;
4838 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004839
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004840 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004841 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07004842 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
4843 != PackageManager.PERMISSION_GRANTED) {
4844 fout.println("Permission Denial: can't dump AccountsManager from from pid="
4845 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
4846 + " without permission " + android.Manifest.permission.DUMP);
4847 return;
4848 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004849 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004850 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07004851
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004852 final List<UserInfo> users = getUserManager().getUsers();
4853 for (UserInfo user : users) {
4854 ipw.println("User " + user + ":");
4855 ipw.increaseIndent();
4856 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
4857 ipw.println();
4858 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08004859 }
4860 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004861
Amith Yamasani04e0d262012-02-14 11:50:53 -08004862 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
4863 String[] args, boolean isCheckinRequest) {
4864 synchronized (userAccounts.cacheLock) {
4865 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004866
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004867 if (isCheckinRequest) {
4868 // This is a checkin request. *Only* upload the account types and the count of each.
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004869 DeDatabaseHelper.dumpAccountsTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004870 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004871 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07004872 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004873 fout.println("Accounts: " + accounts.length);
4874 for (Account account : accounts) {
4875 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004876 }
Fred Quintana307da1a2010-01-21 14:24:20 -08004877
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004878 // Add debug information.
4879 fout.println();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07004880 DebugDbHelper.dumpDebugTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004881 fout.println();
4882 synchronized (mSessions) {
4883 final long now = SystemClock.elapsedRealtime();
4884 fout.println("Active Sessions: " + mSessions.size());
4885 for (Session session : mSessions.values()) {
4886 fout.println(" " + session.toDebugString(now));
4887 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004888 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004889
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004890 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004891 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004892 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004893 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004894 }
4895
Amith Yamasani04e0d262012-02-14 11:50:53 -08004896 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004897 Intent intent, String packageName, final int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004898 long identityToken = clearCallingIdentity();
4899 try {
4900 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4901 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
4902 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004903
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004904 if (intent.getComponent() != null &&
4905 GrantCredentialsPermissionActivity.class.getName().equals(
4906 intent.getComponent().getClassName())) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004907 createNoCredentialsPermissionNotification(account, intent, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004908 } else {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004909 Context contextForUser = getContextForUser(new UserHandle(userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08004910 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07004911 intent.addCategory(String.valueOf(notificationId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004912
Fred Quintana33f889a2009-09-14 17:31:26 -07004913 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004914 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04004915 Notification n = new Notification.Builder(contextForUser)
4916 .setWhen(0)
4917 .setSmallIcon(android.R.drawable.stat_sys_warning)
4918 .setColor(contextForUser.getColor(
4919 com.android.internal.R.color.system_notification_accent_color))
4920 .setContentTitle(String.format(notificationTitleFormat, account.name))
4921 .setContentText(message)
4922 .setContentIntent(PendingIntent.getActivityAsUser(
4923 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004924 null, new UserHandle(userId)))
Chris Wren1ce4b6d2015-06-11 10:19:43 -04004925 .build();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004926 installNotification(notificationId, n, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004927 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004928 } finally {
4929 restoreCallingIdentity(identityToken);
4930 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004931 }
4932
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004933 private void installNotification(int notificationId, final Notification notification,
4934 String packageName, int userId) {
4935 final long token = clearCallingIdentity();
4936 try {
Fyodor Kupolovda993802016-09-21 14:47:10 -07004937 INotificationManager notificationManager = mInjector.getNotificationManager();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004938 try {
4939 notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
4940 notificationId, notification, new int[1], userId);
4941 } catch (RemoteException e) {
4942 /* ignore - local call */
4943 }
4944 } finally {
4945 Binder.restoreCallingIdentity(token);
4946 }
Fred Quintana56285a62010-12-02 14:20:51 -08004947 }
4948
Fyodor Kupolovda993802016-09-21 14:47:10 -07004949 private void cancelNotification(int id, UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004950 cancelNotification(id, mContext.getPackageName(), user);
4951 }
4952
Fyodor Kupolovda993802016-09-21 14:47:10 -07004953 private void cancelNotification(int id, String packageName, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004954 long identityToken = clearCallingIdentity();
4955 try {
Fyodor Kupolovda993802016-09-21 14:47:10 -07004956 INotificationManager service = mInjector.getNotificationManager();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004957 service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
4958 } catch (RemoteException e) {
4959 /* ignore - local call */
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004960 } finally {
4961 restoreCallingIdentity(identityToken);
4962 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004963 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004964
Ian Pedowitz358e51f2016-03-15 17:08:27 +00004965 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
4966 for (String perm : permissions) {
4967 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
4968 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4969 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
4970 }
4971 final int opCode = AppOpsManager.permissionToOpCode(perm);
4972 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
4973 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
4974 return true;
4975 }
4976 }
4977 }
4978 return false;
4979 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004980
Amith Yamasani67df64b2012-12-14 12:09:36 -08004981 private int handleIncomingUser(int userId) {
4982 try {
4983 return ActivityManagerNative.getDefault().handleIncomingUser(
4984 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
4985 } catch (RemoteException re) {
4986 // Shouldn't happen, local.
4987 }
4988 return userId;
4989 }
4990
Christopher Tateccbf84f2013-05-08 15:25:41 -07004991 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004992 final int callingUserId = UserHandle.getUserId(callingUid);
4993
4994 final PackageManager userPackageManager;
4995 try {
4996 userPackageManager = mContext.createPackageContextAsUser(
4997 "android", 0, new UserHandle(callingUserId)).getPackageManager();
4998 } catch (NameNotFoundException e) {
4999 return false;
5000 }
5001
5002 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07005003 for (String name : packages) {
5004 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005005 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08005006 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08005007 && (packageInfo.applicationInfo.privateFlags
5008 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07005009 return true;
5010 }
5011 } catch (PackageManager.NameNotFoundException e) {
5012 return false;
5013 }
5014 }
5015 return false;
5016 }
5017
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005018 private boolean permissionIsGranted(
5019 Account account, String authTokenType, int callerUid, int userId) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005020 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5021 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5022 Log.v(TAG, "Access to " + account + " granted calling uid is system");
5023 }
5024 return true;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005025 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005026
5027 if (isPrivileged(callerUid)) {
5028 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5029 Log.v(TAG, "Access to " + account + " granted calling uid "
5030 + callerUid + " privileged");
5031 }
5032 return true;
5033 }
5034 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
5035 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5036 Log.v(TAG, "Access to " + account + " granted calling uid "
5037 + callerUid + " manages the account");
5038 }
5039 return true;
5040 }
5041 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5042 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5043 Log.v(TAG, "Access to " + account + " granted calling uid "
5044 + callerUid + " user granted access");
5045 }
5046 return true;
5047 }
5048
5049 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5050 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5051 }
5052
5053 return false;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005054 }
5055
Svetoslavf3f02ac2015-09-08 14:36:35 -07005056 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5057 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005058 if (accountType == null) {
5059 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005060 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07005061 return getTypesVisibleToCaller(callingUid, userId,
5062 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005063 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005064 }
5065
5066 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5067 if (accountType == null) {
5068 return false;
5069 } else {
5070 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5071 }
5072 }
5073
Svetoslavf3f02ac2015-09-08 14:36:35 -07005074 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5075 String opPackageName) {
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005076 boolean isPermitted =
5077 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5078 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005079 return getTypesForCaller(callingUid, userId, isPermitted);
5080 }
5081
5082 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5083 return getTypesForCaller(callingUid, userId, false);
5084 }
5085
5086 private List<String> getTypesForCaller(
5087 int callingUid, int userId, boolean isOtherwisePermitted) {
5088 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005089 long identityToken = Binder.clearCallingIdentity();
5090 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5091 try {
5092 serviceInfos = mAuthenticatorCache.getAllServices(userId);
5093 } finally {
5094 Binder.restoreCallingIdentity(identityToken);
5095 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005096 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005097 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005098 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5099 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5100 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005101 }
5102 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005103 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005104 }
5105
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07005106 private boolean isAccountPresentForCaller(String accountName, String accountType) {
5107 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5108 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5109 if (account.name.equals(accountName)) {
5110 return true;
5111 }
5112 }
5113 }
5114 return false;
5115 }
5116
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07005117 private static void checkManageUsersPermission(String message) {
5118 if (ActivityManager.checkComponentPermission(
5119 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5120 != PackageManager.PERMISSION_GRANTED) {
5121 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5122 }
5123 }
5124
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07005125 private static void checkManageOrCreateUsersPermission(String message) {
5126 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5127 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5128 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5129 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5130 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5131 + message);
5132 }
5133 }
5134
Amith Yamasani04e0d262012-02-14 11:50:53 -08005135 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5136 int callerUid) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005137 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005138 return true;
5139 }
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005140 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005141 synchronized (accounts.cacheLock) {
5142 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005143 long grantsCount;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005144 if (authTokenType != null) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005145 grantsCount = DeDatabaseHelper.findMatchingGrantsCount(db, callerUid, authTokenType,
5146 account);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005147 } else {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005148 grantsCount = DeDatabaseHelper.findMatchingGrantsCountAnyToken(db, callerUid,
5149 account);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005150 }
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005151 final boolean permissionGranted = grantsCount > 0;
Svet Ganov890a2102016-08-24 00:08:00 -07005152
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005153 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5154 // TODO: Skip this check when running automated tests. Replace this
5155 // with a more general solution.
5156 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08005157 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005158 + " but ignoring since device is in test harness.");
5159 return true;
5160 }
5161 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005162 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005163 }
5164
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005165 private boolean isSystemUid(int callingUid) {
5166 String[] packages = null;
5167 long ident = Binder.clearCallingIdentity();
5168 try {
5169 packages = mPackageManager.getPackagesForUid(callingUid);
5170 } finally {
5171 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005172 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005173 if (packages != null) {
5174 for (String name : packages) {
5175 try {
5176 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5177 if (packageInfo != null
5178 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5179 != 0) {
5180 return true;
5181 }
5182 } catch (PackageManager.NameNotFoundException e) {
5183 Log.w(TAG, String.format("Could not find package [%s]", name), e);
5184 }
5185 }
5186 } else {
5187 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005188 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005189 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00005190 }
5191
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005192 /** Succeeds if any of the specified permissions are granted. */
5193 private void checkReadAccountsPermitted(
5194 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005195 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07005196 int userId,
5197 String opPackageName) {
5198 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005199 String msg = String.format(
5200 "caller uid %s cannot access %s accounts",
5201 callingUid,
5202 accountType);
5203 Log.w(TAG, " " + msg);
5204 throw new SecurityException(msg);
5205 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005206 }
5207
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005208 private boolean canUserModifyAccounts(int userId, int callingUid) {
5209 // the managing app can always modify accounts
5210 if (isProfileOwner(callingUid)) {
5211 return true;
5212 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005213 if (getUserManager().getUserRestrictions(new UserHandle(userId))
5214 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5215 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005216 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005217 return true;
5218 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005219
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005220 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5221 // the managing app can always modify accounts
5222 if (isProfileOwner(callingUid)) {
5223 return true;
5224 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005225 DevicePolicyManager dpm = (DevicePolicyManager) mContext
5226 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005227 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02005228 if (typesArray == null) {
5229 return true;
5230 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005231 for (String forbiddenType : typesArray) {
5232 if (forbiddenType.equals(accountType)) {
5233 return false;
5234 }
5235 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005236 return true;
5237 }
5238
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005239 private boolean isProfileOwner(int uid) {
5240 final DevicePolicyManagerInternal dpmi =
5241 LocalServices.getService(DevicePolicyManagerInternal.class);
5242 return (dpmi != null)
5243 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5244 }
5245
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08005246 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07005247 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5248 throws RemoteException {
5249 final int callingUid = getCallingUid();
5250
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005251 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07005252 throw new SecurityException();
5253 }
5254
5255 if (value) {
5256 grantAppPermission(account, authTokenType, uid);
5257 } else {
5258 revokeAppPermission(account, authTokenType, uid);
5259 }
5260 }
5261
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005262 /**
5263 * Allow callers with the given uid permission to get credentials for account/authTokenType.
5264 * <p>
5265 * Although this is public it can only be accessed via the AccountManagerService object
5266 * which is in the system. This means we don't need to protect it with permissions.
5267 * @hide
5268 */
Svet Ganov5d09c992016-09-07 09:57:41 -07005269 void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005270 if (account == null || authTokenType == null) {
5271 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005272 return;
5273 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005274 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005275 synchronized (accounts.cacheLock) {
5276 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005277 long accountId = DeDatabaseHelper.findAccountId(db, account);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005278 if (accountId >= 0) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005279 DeDatabaseHelper.insertGrant(db, accountId, authTokenType, uid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005280 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005281 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005282 UserHandle.of(accounts.userId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005283
5284 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005285 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005286
5287 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5288 for (AccountManagerInternal.OnAppPermissionChangeListener listener
5289 : mAppPermissionChangeListeners) {
5290 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5291 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005292 }
5293
5294 /**
5295 * Don't allow callers with the given uid permission to get credentials for
5296 * account/authTokenType.
5297 * <p>
5298 * Although this is public it can only be accessed via the AccountManagerService object
5299 * which is in the system. This means we don't need to protect it with permissions.
5300 * @hide
5301 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005302 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005303 if (account == null || authTokenType == null) {
5304 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005305 return;
5306 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005307 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005308 synchronized (accounts.cacheLock) {
5309 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005310 db.beginTransaction();
5311 try {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005312 long accountId = DeDatabaseHelper.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005313 if (accountId >= 0) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005314 DeDatabaseHelper.deleteGrantsByAccountIdAuthTokenTypeAndUid(
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005315 db, accountId, authTokenType, uid);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005316 db.setTransactionSuccessful();
5317 }
5318 } finally {
5319 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005320 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005321
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005322 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5323 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005324 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005325
5326 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5327 for (AccountManagerInternal.OnAppPermissionChangeListener listener
5328 : mAppPermissionChangeListeners) {
5329 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5330 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005331 }
Fred Quintana56285a62010-12-02 14:20:51 -08005332
Amith Yamasani04e0d262012-02-14 11:50:53 -08005333 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
5334 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005335 if (oldAccountsForType != null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005336 ArrayList<Account> newAccountsList = new ArrayList<>();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005337 for (Account curAccount : oldAccountsForType) {
5338 if (!curAccount.equals(account)) {
5339 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005340 }
5341 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005342 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005343 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005344 } else {
5345 Account[] newAccountsForType = new Account[newAccountsList.size()];
5346 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005347 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005348 }
Fred Quintana56285a62010-12-02 14:20:51 -08005349 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005350 accounts.userDataCache.remove(account);
5351 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005352 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08005353 }
5354
5355 /**
5356 * This assumes that the caller has already checked that the account is not already present.
Svetoslav Ganov57f62592016-09-16 17:29:05 -07005357 * IMPORTANT: The account being inserted will begin to be tracked for access in remote
5358 * processes and if you will return this account to apps you should return the result.
5359 * @return The inserted account which is a new instance that is being tracked.
Fred Quintana56285a62010-12-02 14:20:51 -08005360 */
Svetoslav Ganov57f62592016-09-16 17:29:05 -07005361 private Account insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005362 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005363 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
5364 Account[] newAccountsForType = new Account[oldLength + 1];
5365 if (accountsForType != null) {
5366 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08005367 }
Svet Ganovc1c0d1c2016-09-23 19:15:47 -07005368 String token = account.getAccessId() != null ? account.getAccessId()
5369 : UUID.randomUUID().toString();
5370 newAccountsForType[oldLength] = new Account(account, token);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005371 accounts.accountCache.put(account.type, newAccountsForType);
Svetoslav Ganov57f62592016-09-16 17:29:05 -07005372 return newAccountsForType[oldLength];
Fred Quintana56285a62010-12-02 14:20:51 -08005373 }
5374
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005375 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07005376 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005377 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07005378 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005379 return unfiltered;
5380 }
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07005381 UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
Amith Yamasani0c19bf52013-10-03 10:34:58 -07005382 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005383 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005384 // If any of the packages is a visible listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005385 // otherwise return non-shared accounts only.
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005386 // This might be a temporary way to specify a visible list
5387 String visibleList = mContext.getResources().getString(
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005388 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
5389 for (String packageName : packages) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005390 if (visibleList.contains(";" + packageName + ";")) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005391 return unfiltered;
5392 }
5393 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005394 ArrayList<Account> allowed = new ArrayList<>();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005395 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
5396 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005397 String requiredAccountType = "";
5398 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07005399 // If there's an explicit callingPackage specified, check if that package
5400 // opted in to see restricted accounts.
5401 if (callingPackage != null) {
5402 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005403 if (pi != null && pi.restrictedAccountType != null) {
5404 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07005405 }
5406 } else {
5407 // Otherwise check if the callingUid has a package that has opted in
5408 for (String packageName : packages) {
5409 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
5410 if (pi != null && pi.restrictedAccountType != null) {
5411 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07005412 break;
5413 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005414 }
5415 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005416 } catch (NameNotFoundException nnfe) {
5417 }
5418 for (Account account : unfiltered) {
5419 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005420 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07005421 } else {
5422 boolean found = false;
5423 for (Account shared : sharedAccounts) {
5424 if (shared.equals(account)) {
5425 found = true;
5426 break;
5427 }
5428 }
5429 if (!found) {
5430 allowed.add(account);
5431 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005432 }
5433 }
5434 Account[] filtered = new Account[allowed.size()];
5435 allowed.toArray(filtered);
5436 return filtered;
5437 } else {
5438 return unfiltered;
5439 }
5440 }
5441
Amith Yamasani27db4682013-03-30 17:07:47 -07005442 /*
5443 * packageName can be null. If not null, it should be used to filter out restricted accounts
5444 * that the package is not allowed to access.
5445 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005446 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07005447 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005448 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005449 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005450 if (accounts == null) {
5451 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08005452 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005453 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07005454 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08005455 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005456 } else {
5457 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08005458 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005459 totalLength += accounts.length;
5460 }
5461 if (totalLength == 0) {
5462 return EMPTY_ACCOUNT_ARRAY;
5463 }
5464 Account[] accounts = new Account[totalLength];
5465 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08005466 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005467 System.arraycopy(accountsOfType, 0, accounts, totalLength,
5468 accountsOfType.length);
5469 totalLength += accountsOfType.length;
5470 }
Amith Yamasani27db4682013-03-30 17:07:47 -07005471 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08005472 }
5473 }
5474
Amith Yamasani04e0d262012-02-14 11:50:53 -08005475 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
5476 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005477 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005478 if (userDataForAccount == null) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005479 userDataForAccount = CeDatabaseHelper.findUserExtrasForAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005480 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005481 }
5482 if (value == null) {
5483 userDataForAccount.remove(key);
5484 } else {
5485 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08005486 }
5487 }
5488
Carlos Valdivia91979be2015-05-22 14:11:35 -07005489 protected String readCachedTokenInternal(
5490 UserAccounts accounts,
5491 Account account,
5492 String tokenType,
5493 String callingPackage,
5494 byte[] pkgSigDigest) {
5495 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005496 return accounts.accountTokenCaches.get(
5497 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07005498 }
5499 }
5500
Amith Yamasani04e0d262012-02-14 11:50:53 -08005501 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
5502 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005503 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005504 if (authTokensForAccount == null) {
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005505 authTokensForAccount = CeDatabaseHelper.findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005506 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005507 }
5508 if (value == null) {
5509 authTokensForAccount.remove(key);
5510 } else {
5511 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08005512 }
5513 }
5514
Amith Yamasani04e0d262012-02-14 11:50:53 -08005515 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
5516 String authTokenType) {
5517 synchronized (accounts.cacheLock) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005518 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08005519 if (authTokensForAccount == null) {
5520 // need to populate the cache for this account
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005521 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005522 authTokensForAccount = CeDatabaseHelper.findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005523 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005524 }
5525 return authTokensForAccount.get(authTokenType);
5526 }
5527 }
5528
Simranjit Kohli858511c2016-03-10 18:36:11 +00005529 protected String readUserDataInternalLocked(
5530 UserAccounts accounts, Account account, String key) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005531 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00005532 if (userDataForAccount == null) {
5533 // need to populate the cache for this account
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005534 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1ce01612016-08-26 11:39:07 -07005535 userDataForAccount = CeDatabaseHelper.findUserExtrasForAccount(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00005536 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005537 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00005538 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08005539 }
5540
Kenny Guy07ad8dc2014-09-01 20:56:12 +01005541 private Context getContextForUser(UserHandle user) {
5542 try {
5543 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
5544 } catch (NameNotFoundException e) {
5545 // Default to mContext, not finding the package system is running as is unlikely.
5546 return mContext;
5547 }
5548 }
Sandra Kwan78812282015-11-04 11:19:47 -08005549
5550 private void sendResponse(IAccountManagerResponse response, Bundle result) {
5551 try {
5552 response.onResult(result);
5553 } catch (RemoteException e) {
5554 // if the caller is dead then there is no one to care about remote
5555 // exceptions
5556 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5557 Log.v(TAG, "failure while notifying response", e);
5558 }
5559 }
5560 }
5561
5562 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
5563 String errorMessage) {
5564 try {
5565 response.onError(errorCode, errorMessage);
5566 } catch (RemoteException e) {
5567 // if the caller is dead then there is no one to care about remote
5568 // exceptions
5569 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5570 Log.v(TAG, "failure while notifying response", e);
5571 }
5572 }
5573 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005574
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005575 private final class AccountManagerInternalImpl extends AccountManagerInternal {
Svet Ganov5d09c992016-09-07 09:57:41 -07005576 private final Object mLock = new Object();
5577
5578 @GuardedBy("mLock")
5579 private AccountManagerBackupHelper mBackupHelper;
5580
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005581 @Override
5582 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
5583 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
5584 if (account == null) {
5585 Slog.w(TAG, "account cannot be null");
5586 return;
5587 }
5588 if (packageName == null) {
5589 Slog.w(TAG, "packageName cannot be null");
5590 return;
5591 }
5592 if (userId < UserHandle.USER_SYSTEM) {
5593 Slog.w(TAG, "user id must be concrete");
5594 return;
5595 }
5596 if (callback == null) {
5597 Slog.w(TAG, "callback cannot be null");
5598 return;
5599 }
5600
Svet Ganovf6d424f12016-09-20 20:18:53 -07005601 if (AccountManagerService.this.hasAccountAccess(account, packageName,
5602 new UserHandle(userId))) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005603 Bundle result = new Bundle();
5604 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
5605 callback.sendResult(result);
5606 return;
5607 }
5608
5609 final int uid;
5610 try {
5611 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
5612 } catch (NameNotFoundException e) {
5613 Slog.e(TAG, "Unknown package " + packageName);
5614 return;
5615 }
5616
5617 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
Svet Ganovf6d424f12016-09-20 20:18:53 -07005618 final UserAccounts userAccounts;
5619 synchronized (mUsers) {
5620 userAccounts = mUsers.get(userId);
5621 }
5622 doNotification(userAccounts, account, null, intent, packageName, userId);
5623 }
5624
5625 @Override
5626 public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) {
5627 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5628 mAppPermissionChangeListeners.add(listener);
5629 }
5630
5631 @Override
5632 public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) {
5633 return AccountManagerService.this.hasAccountAccess(account, null, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005634 }
Svet Ganov5d09c992016-09-07 09:57:41 -07005635
5636 @Override
5637 public byte[] backupAccountAccessPermissions(int userId) {
5638 synchronized (mLock) {
5639 if (mBackupHelper == null) {
5640 mBackupHelper = new AccountManagerBackupHelper(
5641 AccountManagerService.this, this);
5642 }
5643 return mBackupHelper.backupAccountAccessPermissions(userId);
5644 }
5645 }
5646
5647 @Override
5648 public void restoreAccountAccessPermissions(byte[] data, int userId) {
5649 synchronized (mLock) {
5650 if (mBackupHelper == null) {
5651 mBackupHelper = new AccountManagerBackupHelper(
5652 AccountManagerService.this, this);
5653 }
5654 mBackupHelper.restoreAccountAccessPermissions(data, userId);
5655 }
5656 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005657 }
Fyodor Kupolovda993802016-09-21 14:47:10 -07005658
5659 @VisibleForTesting
5660 static class Injector {
5661 private final Context mContext;
5662
5663 public Injector(Context context) {
5664 mContext = context;
5665 }
5666
5667 Looper getMessageHandlerLooper() {
5668 ServiceThread serviceThread = new ServiceThread(TAG,
5669 android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
5670 serviceThread.start();
5671 return serviceThread.getLooper();
5672 }
5673
5674 Context getContext() {
5675 return mContext;
5676 }
5677
5678 void addLocalService(AccountManagerInternal service) {
5679 LocalServices.addService(AccountManagerInternal.class, service);
5680 }
5681
5682 String getDeDatabaseName(int userId) {
5683 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
5684 AccountsDb.DE_DATABASE_NAME);
5685 return databaseFile.getPath();
5686 }
5687
5688 String getCeDatabaseName(int userId) {
5689 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
5690 AccountsDb.CE_DATABASE_NAME);
5691 return databaseFile.getPath();
5692 }
5693
5694 String getPreNDatabaseName(int userId) {
5695 File systemDir = Environment.getDataSystemDirectory();
5696 File databaseFile = new File(Environment.getUserSystemDirectory(userId),
5697 PRE_N_DATABASE_NAME);
5698 if (userId == 0) {
5699 // Migrate old file, if it exists, to the new location.
5700 // Make sure the new file doesn't already exist. A dummy file could have been
5701 // accidentally created in the old location, causing the new one to become corrupted
5702 // as well.
5703 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
5704 if (oldFile.exists() && !databaseFile.exists()) {
5705 // Check for use directory; create if it doesn't exist, else renameTo will fail
5706 File userDir = Environment.getUserSystemDirectory(userId);
5707 if (!userDir.exists()) {
5708 if (!userDir.mkdirs()) {
5709 throw new IllegalStateException("User dir cannot be created: " + userDir);
5710 }
5711 }
5712 if (!oldFile.renameTo(databaseFile)) {
5713 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
5714 }
5715 }
5716 }
5717 return databaseFile.getPath();
5718 }
5719
5720 IAccountAuthenticatorCache getAccountAuthenticatorCache() {
5721 return new AccountAuthenticatorCache(mContext);
5722 }
5723
5724 INotificationManager getNotificationManager() {
5725 return NotificationManager.getService();
5726 }
5727 }
Fred Quintana60307342009-03-24 22:48:12 -07005728}