blob: caeb0c6380d5bc982ae9958cdba7e8c1e708c176 [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;
25import android.accounts.AuthenticatorDescription;
Amith Yamasani23c8b962013-04-10 13:37:18 -070026import android.accounts.CantAddAccountActivity;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080027import android.accounts.GrantCredentialsPermissionActivity;
28import android.accounts.IAccountAuthenticator;
29import android.accounts.IAccountAuthenticatorResponse;
30import android.accounts.IAccountManager;
31import android.accounts.IAccountManagerResponse;
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -070032import android.annotation.NonNull;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080033import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070034import android.app.ActivityManagerNative;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070035import android.app.AppGlobals;
Svetoslavf3f02ac2015-09-08 14:36:35 -070036import android.app.AppOpsManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070037import android.app.Notification;
38import android.app.NotificationManager;
39import android.app.PendingIntent;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000040import android.app.admin.DeviceAdminInfo;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010041import android.app.admin.DevicePolicyManager;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000042import android.app.admin.DevicePolicyManagerInternal;
Fred Quintanaa698f422009-04-08 19:14:54 -070043import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070044import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070045import android.content.ContentValues;
46import android.content.Context;
47import android.content.Intent;
48import android.content.IntentFilter;
Fred Quintanab839afc2009-10-14 15:57:28 -070049import android.content.ServiceConnection;
Doug Zongker885cfc232009-10-21 16:52:44 -070050import android.content.pm.ApplicationInfo;
51import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070052import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070053import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070054import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070055import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070056import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070057import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070058import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070059import android.database.Cursor;
60import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070061import android.database.sqlite.SQLiteDatabase;
62import android.database.sqlite.SQLiteOpenHelper;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070063import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070064import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070065import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080066import android.os.Environment;
Fred Quintanaa698f422009-04-08 19:14:54 -070067import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070068import android.os.IBinder;
69import android.os.Looper;
70import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070071import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070072import android.os.Process;
Fred Quintanaa698f422009-04-08 19:14:54 -070073import android.os.RemoteException;
74import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070075import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070076import android.os.UserManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070077import android.text.TextUtils;
78import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070079import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070080import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080081import android.util.SparseArray;
Fred Quintana60307342009-03-24 22:48:12 -070082
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070083import com.android.internal.R;
Amith Yamasani67df64b2012-12-14 12:09:36 -080084import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -080085import com.android.internal.util.IndentingPrintWriter;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070086import com.android.server.FgThread;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000087import com.android.server.LocalServices;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070088import com.google.android.collect.Lists;
89import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070090
Oscar Montemayora8529f62009-11-18 10:14:20 -080091import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -070092import java.io.FileDescriptor;
93import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -080094import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -070095import java.security.MessageDigest;
96import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070097import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -070098import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -080099import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700100import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700101import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700102import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700103import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -0800104import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700105import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800106import java.util.Map;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700107import java.util.concurrent.atomic.AtomicInteger;
108import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700109
Fred Quintana60307342009-03-24 22:48:12 -0700110/**
111 * A system service that provides account, password, and authtoken management for all
112 * accounts on the device. Some of these calls are implemented with the help of the corresponding
113 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
114 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700115 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700116 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700117 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700118public class AccountManagerService
119 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800120 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Sandra Kwan78812282015-11-04 11:19:47 -0800121
Fred Quintana60307342009-03-24 22:48:12 -0700122 private static final String TAG = "AccountManagerService";
123
Fred Quintana60307342009-03-24 22:48:12 -0700124 private static final String DATABASE_NAME = "accounts.db";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700125 private static final int DATABASE_VERSION = 8;
126
127 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700128
129 private final Context mContext;
130
Fred Quintana56285a62010-12-02 14:20:51 -0800131 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700132 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700133 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -0800134
Fred Quintana60307342009-03-24 22:48:12 -0700135 private final MessageHandler mMessageHandler;
136
137 // Messages that can be sent on mHandler
138 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700139 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700140
Fred Quintana56285a62010-12-02 14:20:51 -0800141 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700142
143 private static final String TABLE_ACCOUNTS = "accounts";
144 private static final String ACCOUNTS_ID = "_id";
145 private static final String ACCOUNTS_NAME = "name";
146 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700147 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700148 private static final String ACCOUNTS_PASSWORD = "password";
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700149 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800150 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
151 "last_password_entry_time_millis_epoch";
Fred Quintana60307342009-03-24 22:48:12 -0700152
153 private static final String TABLE_AUTHTOKENS = "authtokens";
154 private static final String AUTHTOKENS_ID = "_id";
155 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
156 private static final String AUTHTOKENS_TYPE = "type";
157 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
158
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700159 private static final String TABLE_GRANTS = "grants";
160 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
161 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
162 private static final String GRANTS_GRANTEE_UID = "uid";
163
Fred Quintana60307342009-03-24 22:48:12 -0700164 private static final String TABLE_EXTRAS = "extras";
165 private static final String EXTRAS_ID = "_id";
166 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
167 private static final String EXTRAS_KEY = "key";
168 private static final String EXTRAS_VALUE = "value";
169
170 private static final String TABLE_META = "meta";
171 private static final String META_KEY = "key";
172 private static final String META_VALUE = "value";
173
Amith Yamasani67df64b2012-12-14 12:09:36 -0800174 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
175
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700176 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
177 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700178 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800179
Carlos Valdivia91979be2015-05-22 14:11:35 -0700180 static {
181 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
182 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
183 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700184
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700185 private static final String COUNT_OF_MATCHING_GRANTS = ""
186 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
187 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
188 + " AND " + GRANTS_GRANTEE_UID + "=?"
189 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
190 + " AND " + ACCOUNTS_NAME + "=?"
191 + " AND " + ACCOUNTS_TYPE + "=?";
192
Fred Quintana56285a62010-12-02 14:20:51 -0800193 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
194 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
Carlos Valdivia91979be2015-05-22 14:11:35 -0700195
Fred Quintana56285a62010-12-02 14:20:51 -0800196 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
197 AUTHTOKENS_AUTHTOKEN};
198
199 private static final String SELECTION_USERDATA_BY_ACCOUNT =
200 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
201 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
202
Fred Quintanaa698f422009-04-08 19:14:54 -0700203 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700204 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
205
Amith Yamasani04e0d262012-02-14 11:50:53 -0800206 static class UserAccounts {
207 private final int userId;
208 private final DatabaseHelper openHelper;
209 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
210 credentialsPermissionNotificationIds =
211 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
212 private final HashMap<Account, Integer> signinRequiredNotificationIds =
213 new HashMap<Account, Integer>();
214 private final Object cacheLock = new Object();
215 /** protected by the {@link #cacheLock} */
Amith Yamasanib483a992012-05-22 13:14:25 -0700216 private final HashMap<String, Account[]> accountCache =
217 new LinkedHashMap<String, Account[]>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800218 /** protected by the {@link #cacheLock} */
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800219 private final HashMap<Account, HashMap<String, String>> userDataCache =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800220 new HashMap<Account, HashMap<String, String>>();
221 /** protected by the {@link #cacheLock} */
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800222 private final HashMap<Account, HashMap<String, String>> authTokenCache =
Amith Yamasani04e0d262012-02-14 11:50:53 -0800223 new HashMap<Account, HashMap<String, String>>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700224
225 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700226 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700227
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700228 /**
229 * protected by the {@link #cacheLock}
230 *
231 * Caches the previous names associated with an account. Previous names
232 * should be cached because we expect that when an Account is renamed,
233 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
234 * want to know if the accounts they care about have been renamed.
235 *
236 * The previous names are wrapped in an {@link AtomicReference} so that
237 * we can distinguish between those accounts with no previous names and
238 * those whose previous names haven't been cached (yet).
239 */
240 private final HashMap<Account, AtomicReference<String>> previousNameCache =
241 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800242
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700243 private int debugDbInsertionPoint = -1;
244 private SQLiteStatement statementForLogging;
245
Amith Yamasani04e0d262012-02-14 11:50:53 -0800246 UserAccounts(Context context, int userId) {
247 this.userId = userId;
248 synchronized (cacheLock) {
249 openHelper = new DatabaseHelper(context, userId);
250 }
251 }
252 }
253
254 private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
255
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700256 private static AtomicReference<AccountManagerService> sThis =
257 new AtomicReference<AccountManagerService>();
Fred Quintana31957f12009-10-21 13:43:10 -0700258 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700259
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700260 /**
261 * This should only be called by system code. One should only call this after the service
262 * has started.
263 * @return a reference to the AccountManagerService instance
264 * @hide
265 */
266 public static AccountManagerService getSingleton() {
267 return sThis.get();
268 }
Fred Quintana60307342009-03-24 22:48:12 -0700269
Fred Quintana56285a62010-12-02 14:20:51 -0800270 public AccountManagerService(Context context) {
271 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700272 }
273
Fred Quintana56285a62010-12-02 14:20:51 -0800274 public AccountManagerService(Context context, PackageManager packageManager,
275 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700276 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800277 mPackageManager = packageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700278 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fred Quintana60307342009-03-24 22:48:12 -0700279
Dianne Hackborn8d044e82013-04-30 17:24:15 -0700280 mMessageHandler = new MessageHandler(FgThread.get().getLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700281
Fred Quintana56285a62010-12-02 14:20:51 -0800282 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800283 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700284
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700285 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800286
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800287 IntentFilter intentFilter = new IntentFilter();
288 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
289 intentFilter.addDataScheme("package");
290 mContext.registerReceiver(new BroadcastReceiver() {
291 @Override
292 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700293 // Don't delete accounts when updating a authenticator's
294 // package.
295 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700296 /* Purging data requires file io, don't block the main thread. This is probably
297 * less than ideal because we are introducing a race condition where old grants
298 * could be exercised until they are purged. But that race condition existed
299 * anyway with the broadcast receiver.
300 *
301 * Ideally, we would completely clear the cache, purge data from the database,
302 * and then rebuild the cache. All under the cache lock. But that change is too
303 * large at this point.
304 */
305 Runnable r = new Runnable() {
306 @Override
307 public void run() {
308 purgeOldGrantsAll();
309 }
310 };
311 new Thread(r).start();
Carlos Valdivia23f58262014-09-05 10:52:41 -0700312 }
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800313 }
314 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800315
Amith Yamasani13593602012-03-22 16:16:17 -0700316 IntentFilter userFilter = new IntentFilter();
317 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Fyodor Kupolov041232a2016-02-22 15:01:45 -0800318 userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800319 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700320 @Override
321 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800322 String action = intent.getAction();
323 if (Intent.ACTION_USER_REMOVED.equals(action)) {
324 onUserRemoved(intent);
Fyodor Kupolov041232a2016-02-22 15:01:45 -0800325 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
326 onUserUnlocked(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800327 }
Amith Yamasani13593602012-03-22 16:16:17 -0700328 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800329 }, UserHandle.ALL, userFilter, null, null);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800330 }
331
Dianne Hackborn164371f2013-10-01 19:10:13 -0700332 @Override
333 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
334 throws RemoteException {
335 try {
336 return super.onTransact(code, data, reply, flags);
337 } catch (RuntimeException e) {
338 // The account manager only throws security exceptions, so let's
339 // log all others.
340 if (!(e instanceof SecurityException)) {
341 Slog.wtf(TAG, "Account Manager Crash", e);
342 }
343 throw e;
344 }
345 }
346
Kenny Root26ff6622012-07-30 12:58:03 -0700347 public void systemReady() {
Kenny Root26ff6622012-07-30 12:58:03 -0700348 }
349
Amith Yamasani258848d2012-08-10 17:06:33 -0700350 private UserManager getUserManager() {
351 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700352 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -0700353 }
354 return mUserManager;
355 }
356
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700357 /**
358 * Validate internal set of accounts against installed authenticators for
359 * given user. Clears cached authenticators before validating.
360 */
361 public void validateAccounts(int userId) {
362 final UserAccounts accounts = getUserAccounts(userId);
363
364 // Invalidate user-specific cache to make sure we catch any
365 // removed authenticators.
366 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
367 }
368
369 /**
370 * Validate internal set of accounts against installed authenticators for
371 * given user. Clear cached authenticators before validating when requested.
372 */
373 private void validateAccountsInternal(
374 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
375 if (invalidateAuthenticatorCache) {
376 mAuthenticatorCache.invalidateCache(accounts.userId);
377 }
378
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700379 final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet();
380 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
381 mAuthenticatorCache.getAllServices(accounts.userId)) {
382 knownAuth.add(service.type);
383 }
384
Amith Yamasani04e0d262012-02-14 11:50:53 -0800385 synchronized (accounts.cacheLock) {
386 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800387 boolean accountDeleted = false;
388 Cursor cursor = db.query(TABLE_ACCOUNTS,
389 new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
Marvin Paul48fcd4e2014-12-01 18:26:07 -0800390 null, null, null, null, ACCOUNTS_ID);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800391 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800392 accounts.accountCache.clear();
Fred Quintana56285a62010-12-02 14:20:51 -0800393 final HashMap<String, ArrayList<String>> accountNamesByType =
Amith Yamasanib483a992012-05-22 13:14:25 -0700394 new LinkedHashMap<String, ArrayList<String>>();
Fred Quintana56285a62010-12-02 14:20:51 -0800395 while (cursor.moveToNext()) {
396 final long accountId = cursor.getLong(0);
397 final String accountType = cursor.getString(1);
398 final String accountName = cursor.getString(2);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700399
400 if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700401 Slog.w(TAG, "deleting account " + accountName + " because type "
Fred Quintana56285a62010-12-02 14:20:51 -0800402 + accountType + " no longer has a registered authenticator");
403 db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
404 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700405
406 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
407 accountId, accounts);
408
Fred Quintana56285a62010-12-02 14:20:51 -0800409 final Account account = new Account(accountName, accountType);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800410 accounts.userDataCache.remove(account);
411 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -0700412 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -0800413 } else {
414 ArrayList<String> accountNames = accountNamesByType.get(accountType);
415 if (accountNames == null) {
416 accountNames = new ArrayList<String>();
417 accountNamesByType.put(accountType, accountNames);
418 }
419 accountNames.add(accountName);
420 }
421 }
Andy McFadden2f362292012-01-20 14:43:38 -0800422 for (Map.Entry<String, ArrayList<String>> cur
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800423 : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -0800424 final String accountType = cur.getKey();
425 final ArrayList<String> accountNames = cur.getValue();
426 final Account[] accountsForType = new Account[accountNames.size()];
427 int i = 0;
428 for (String accountName : accountNames) {
429 accountsForType[i] = new Account(accountName, accountType);
430 ++i;
431 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800432 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800433 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800434 } finally {
435 cursor.close();
436 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800437 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800438 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800439 }
440 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700441 }
442
Amith Yamasani04e0d262012-02-14 11:50:53 -0800443 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700444 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -0800445 }
446
447 protected UserAccounts getUserAccounts(int userId) {
448 synchronized (mUsers) {
449 UserAccounts accounts = mUsers.get(userId);
450 if (accounts == null) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700451 accounts = new UserAccounts(mContext, userId);
452 initializeDebugDbSizeAndCompileSqlStatementForLogging(
453 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800454 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700455 purgeOldGrants(accounts);
456 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800457 }
458 return accounts;
459 }
460 }
461
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700462 private void purgeOldGrantsAll() {
463 synchronized (mUsers) {
464 for (int i = 0; i < mUsers.size(); i++) {
465 purgeOldGrants(mUsers.valueAt(i));
466 }
467 }
468 }
469
470 private void purgeOldGrants(UserAccounts accounts) {
471 synchronized (accounts.cacheLock) {
472 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
473 final Cursor cursor = db.query(TABLE_GRANTS,
474 new String[]{GRANTS_GRANTEE_UID},
475 null, null, GRANTS_GRANTEE_UID, null, null);
476 try {
477 while (cursor.moveToNext()) {
478 final int uid = cursor.getInt(0);
479 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
480 if (packageExists) {
481 continue;
482 }
483 Log.d(TAG, "deleting grants for UID " + uid
484 + " because its package is no longer installed");
485 db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
486 new String[]{Integer.toString(uid)});
487 }
488 } finally {
489 cursor.close();
490 }
491 }
492 }
493
Amith Yamasani13593602012-03-22 16:16:17 -0700494 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700495 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -0700496 if (userId < 1) return;
497
498 UserAccounts accounts;
499 synchronized (mUsers) {
500 accounts = mUsers.get(userId);
501 mUsers.remove(userId);
502 }
503 if (accounts == null) {
504 File dbFile = new File(getDatabaseName(userId));
505 dbFile.delete();
506 return;
507 }
508
509 synchronized (accounts.cacheLock) {
510 accounts.openHelper.close();
511 File dbFile = new File(getDatabaseName(userId));
512 dbFile.delete();
513 }
514 }
515
Fyodor Kupolov041232a2016-02-22 15:01:45 -0800516 private void onUserUnlocked(Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800517 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
518 if (userId < 1) return;
519
520 // Check if there's a shared account that needs to be created as an account
521 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
522 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700523 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700524 int parentUserId = UserManager.isSplitSystemUser()
525 ? mUserManager.getUserInfo(userId).restrictedProfileParentId
526 : UserHandle.USER_SYSTEM;
527 if (parentUserId < 0) {
528 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
529 return;
530 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800531 for (Account sa : sharedAccounts) {
532 if (ArrayUtils.contains(accounts, sa)) continue;
533 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700534 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800535 }
536 }
537
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700538 @Override
539 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700540 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -0700541 }
542
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800543 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -0700544 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700545 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800546 if (Log.isLoggable(TAG, Log.VERBOSE)) {
547 Log.v(TAG, "getPassword: " + account
548 + ", caller's uid " + Binder.getCallingUid()
549 + ", pid " + Binder.getCallingPid());
550 }
Fred Quintana382601f2010-03-25 12:25:10 -0700551 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000552 int userId = UserHandle.getCallingUserId();
553 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700554 String msg = String.format(
555 "uid %s cannot get secrets for accounts of type: %s",
556 callingUid,
557 account.type);
558 throw new SecurityException(msg);
559 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700560 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700561 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700562 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800563 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700564 } finally {
565 restoreCallingIdentity(identityToken);
566 }
567 }
568
Amith Yamasani04e0d262012-02-14 11:50:53 -0800569 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -0700570 if (account == null) {
571 return null;
572 }
573
Amith Yamasani04e0d262012-02-14 11:50:53 -0800574 synchronized (accounts.cacheLock) {
575 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800576 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
577 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
578 new String[]{account.name, account.type}, null, null, null);
579 try {
580 if (cursor.moveToNext()) {
581 return cursor.getString(0);
582 }
583 return null;
584 } finally {
585 cursor.close();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700586 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700587 }
588 }
589
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800590 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700591 public String getPreviousName(Account account) {
592 if (Log.isLoggable(TAG, Log.VERBOSE)) {
593 Log.v(TAG, "getPreviousName: " + account
594 + ", caller's uid " + Binder.getCallingUid()
595 + ", pid " + Binder.getCallingPid());
596 }
597 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700598 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700599 long identityToken = clearCallingIdentity();
600 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700601 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700602 return readPreviousNameInternal(accounts, account);
603 } finally {
604 restoreCallingIdentity(identityToken);
605 }
606 }
607
608 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
609 if (account == null) {
610 return null;
611 }
612 synchronized (accounts.cacheLock) {
613 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
614 if (previousNameRef == null) {
615 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
616 Cursor cursor = db.query(
617 TABLE_ACCOUNTS,
618 new String[]{ ACCOUNTS_PREVIOUS_NAME },
619 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
620 new String[] { account.name, account.type },
621 null,
622 null,
623 null);
624 try {
625 if (cursor.moveToNext()) {
626 String previousName = cursor.getString(0);
627 previousNameRef = new AtomicReference<String>(previousName);
628 accounts.previousNameCache.put(account, previousNameRef);
629 return previousName;
630 } else {
631 return null;
632 }
633 } finally {
634 cursor.close();
635 }
636 } else {
637 return previousNameRef.get();
638 }
639 }
640 }
641
642 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700643 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700644 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800645 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700646 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
647 account, key, callingUid, Binder.getCallingPid());
648 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -0800649 }
Fred Quintana382601f2010-03-25 12:25:10 -0700650 if (account == null) throw new IllegalArgumentException("account is null");
651 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000652 int userId = UserHandle.getCallingUserId();
653 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700654 String msg = String.format(
655 "uid %s cannot get user data for accounts of type: %s",
656 callingUid,
657 account.type);
658 throw new SecurityException(msg);
659 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700660 long identityToken = clearCallingIdentity();
661 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700662 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +0000663 return readUserDataInternal(accounts, account, key);
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700664 } finally {
665 restoreCallingIdentity(identityToken);
666 }
667 }
668
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -0800669 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100670 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700671 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800672 if (Log.isLoggable(TAG, Log.VERBOSE)) {
673 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100674 + "for user id " + userId
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700675 + "caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -0800676 + ", pid " + Binder.getCallingPid());
677 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100678 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700679 if (isCrossUser(callingUid, userId)) {
680 throw new SecurityException(
681 String.format(
682 "User %s tying to get authenticator types for %s" ,
683 UserHandle.getCallingUserId(),
684 userId));
685 }
686
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700687 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700688 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000689 return getAuthenticatorTypesInternal(userId);
690
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700691 } finally {
692 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -0700693 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700694 }
695
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000696 /**
697 * Should only be called inside of a clearCallingIdentity block.
698 */
699 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
700 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
701 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
702 AuthenticatorDescription[] types =
703 new AuthenticatorDescription[authenticatorCollection.size()];
704 int i = 0;
705 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
706 : authenticatorCollection) {
707 types[i] = authenticator.type;
708 i++;
709 }
710 return types;
711 }
712
713
714
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700715 private boolean isCrossUser(int callingUid, int userId) {
716 return (userId != UserHandle.getCallingUserId()
717 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100718 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700719 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
720 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +0100721 }
722
Jatin Lodhia3df7d692013-03-27 10:57:23 -0700723 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -0700724 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700725 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -0800726 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -0700727 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700728 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -0800729 + ", pid " + Binder.getCallingPid());
730 }
Fred Quintana382601f2010-03-25 12:25:10 -0700731 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000732 int userId = UserHandle.getCallingUserId();
733 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700734 String msg = String.format(
735 "uid %s cannot explicitly add accounts of type: %s",
736 callingUid,
737 account.type);
738 throw new SecurityException(msg);
739 }
Jatin Lodhia3df7d692013-03-27 10:57:23 -0700740 /*
741 * Child users are not allowed to add accounts. Only the accounts that are
742 * shared by the parent profile can be added to child profile.
743 *
744 * TODO: Only allow accounts that were shared to be added by
745 * a limited user.
746 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700747
Fred Quintana60307342009-03-24 22:48:12 -0700748 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700749 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -0700750 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700751 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000752 return addAccountInternal(accounts, account, password, extras, false, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -0700753 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -0700754 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -0700755 }
756 }
757
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000758 @Override
759 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700760 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700761 int callingUid = Binder.getCallingUid();
762 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
763 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000764 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700765 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800766 final UserAccounts fromAccounts = getUserAccounts(userFrom);
767 final UserAccounts toAccounts = getUserAccounts(userTo);
768 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000769 if (response != null) {
770 Bundle result = new Bundle();
771 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
772 try {
773 response.onResult(result);
774 } catch (RemoteException e) {
775 Slog.w(TAG, "Failed to report error back to the client." + e);
776 }
777 }
778 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800779 }
780
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000781 Slog.d(TAG, "Copying account " + account.name
782 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800783 long identityToken = clearCallingIdentity();
784 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000785 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800786 false /* stripAuthTokenFromResult */, account.name,
787 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700788 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800789 protected String toDebugString(long now) {
790 return super.toDebugString(now) + ", getAccountCredentialsForClone"
791 + ", " + account.type;
792 }
793
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700794 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800795 public void run() throws RemoteException {
796 mAuthenticator.getAccountCredentialsForCloning(this, account);
797 }
798
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700799 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800800 public void onResult(Bundle result) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000801 if (result != null
802 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
803 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700804 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800805 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800806 super.onResult(result);
807 }
808 }
809 }.bind();
810 } finally {
811 restoreCallingIdentity(identityToken);
812 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800813 }
814
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800815 @Override
816 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700817 final int callingUid = Binder.getCallingUid();
818 if (Log.isLoggable(TAG, Log.VERBOSE)) {
819 String msg = String.format(
820 "accountAuthenticated( account: %s, callerUid: %s)",
821 account,
822 callingUid);
823 Log.v(TAG, msg);
824 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800825 if (account == null) {
826 throw new IllegalArgumentException("account is null");
827 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000828 int userId = UserHandle.getCallingUserId();
829 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -0700830 String msg = String.format(
831 "uid %s cannot notify authentication for accounts of type: %s",
832 callingUid,
833 account.type);
834 throw new SecurityException(msg);
835 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000836
Benjamin Franzb6c0ce42015-11-05 10:06:51 +0000837 if (!canUserModifyAccounts(userId, callingUid) ||
838 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800839 return false;
840 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000841
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700842 long identityToken = clearCallingIdentity();
843 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +0000844 UserAccounts accounts = getUserAccounts(userId);
845 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -0700846 } finally {
847 restoreCallingIdentity(identityToken);
848 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -0700849 }
850
851 private boolean updateLastAuthenticatedTime(Account account) {
852 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800853 synchronized (accounts.cacheLock) {
854 final ContentValues values = new ContentValues();
855 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
856 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
857 int i = db.update(
858 TABLE_ACCOUNTS,
859 values,
860 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
861 new String[] {
862 account.name, account.type
863 });
864 if (i > 0) {
865 return true;
866 }
867 }
868 return false;
869 }
870
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000871 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700872 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
873 final int parentUserId){
Amith Yamasani67df64b2012-12-14 12:09:36 -0800874 long id = clearCallingIdentity();
875 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000876 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800877 false /* stripAuthTokenFromResult */, account.name,
878 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700879 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800880 protected String toDebugString(long now) {
881 return super.toDebugString(now) + ", getAccountCredentialsForClone"
882 + ", " + account.type;
883 }
884
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700885 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800886 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700887 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700888 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700889 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -0700890 for (Account acc : getAccounts(parentUserId,
891 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700892 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000893 mAuthenticator.addAccountFromCredentials(
894 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700895 break;
896 }
897 }
898 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800899 }
900
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700901 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800902 public void onResult(Bundle result) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +0000903 // TODO: Anything to do if if succedded?
904 // TODO: If it failed: Show error notification? Should we remove the shadow
905 // account to avoid retries?
906 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800907 }
908
Carlos Valdivia5bab9da2013-09-29 05:11:56 -0700909 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -0800910 public void onError(int errorCode, String errorMessage) {
911 super.onError(errorCode, errorMessage);
912 // TODO: Show error notification to user
913 // TODO: Should we remove the shadow account so that it doesn't keep trying?
914 }
915
916 }.bind();
917 } finally {
918 restoreCallingIdentity(id);
919 }
920 }
921
Amith Yamasani04e0d262012-02-14 11:50:53 -0800922 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700923 Bundle extras, boolean restricted, int callingUid) {
Fred Quintana743dfad2010-07-15 10:59:25 -0700924 if (account == null) {
925 return false;
926 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800927 synchronized (accounts.cacheLock) {
928 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800929 db.beginTransaction();
930 try {
931 long numMatches = DatabaseUtils.longForQuery(db,
932 "select count(*) from " + TABLE_ACCOUNTS
933 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
934 new String[]{account.name, account.type});
935 if (numMatches > 0) {
936 Log.w(TAG, "insertAccountIntoDatabase: " + account
937 + ", skipping since the account already exists");
938 return false;
939 }
940 ContentValues values = new ContentValues();
941 values.put(ACCOUNTS_NAME, account.name);
942 values.put(ACCOUNTS_TYPE, account.type);
943 values.put(ACCOUNTS_PASSWORD, password);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800944 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800945 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
946 if (accountId < 0) {
947 Log.w(TAG, "insertAccountIntoDatabase: " + account
948 + ", skipping the DB insert failed");
949 return false;
950 }
951 if (extras != null) {
952 for (String key : extras.keySet()) {
953 final String value = extras.getString(key);
954 if (insertExtraLocked(db, accountId, key, value) < 0) {
955 Log.w(TAG, "insertAccountIntoDatabase: " + account
956 + ", skipping since insertExtra failed for key " + key);
957 return false;
958 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700959 }
960 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800961 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700962
963 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
964 accounts, callingUid);
965
Amith Yamasani04e0d262012-02-14 11:50:53 -0800966 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800967 } finally {
968 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700969 }
Amith Yamasani04e0d262012-02-14 11:50:53 -0800970 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700971 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700972 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
973 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -0700974 }
975 return true;
976 }
977
978 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700979 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -0700980 * running, then clone the account too.
981 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700982 *
Amith Yamasani5be347b2013-03-31 17:44:31 -0700983 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700984 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -0700985 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -0700986 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -0700987 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -0700988 addSharedAccountAsUser(account, user.id);
Fyodor Kupolov041232a2016-02-22 15:01:45 -0800989 if (mUserManager.isUserUnlocked(user.id)) {
990 mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
991 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -0700992 }
993 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -0700994 }
995 }
996
Fred Quintanaf9f240e2011-02-24 18:27:50 -0800997 private long insertExtraLocked(SQLiteDatabase db, long accountId, String key, String value) {
Fred Quintana60307342009-03-24 22:48:12 -0700998 ContentValues values = new ContentValues();
999 values.put(EXTRAS_KEY, key);
1000 values.put(EXTRAS_ACCOUNTS_ID, accountId);
1001 values.put(EXTRAS_VALUE, value);
1002 return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
1003 }
1004
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001005 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001006 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001007 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001008 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001009 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1010 Log.v(TAG, "hasFeatures: " + account
1011 + ", response " + response
1012 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001013 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001014 + ", pid " + Binder.getCallingPid());
1015 }
Fred Quintana382601f2010-03-25 12:25:10 -07001016 if (response == null) throw new IllegalArgumentException("response is null");
1017 if (account == null) throw new IllegalArgumentException("account is null");
1018 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001019 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001020 checkReadAccountsPermitted(callingUid, account.type, userId,
1021 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001022
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001023 long identityToken = clearCallingIdentity();
1024 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001025 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001026 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001027 } finally {
1028 restoreCallingIdentity(identityToken);
1029 }
1030 }
1031
1032 private class TestFeaturesSession extends Session {
1033 private final String[] mFeatures;
1034 private final Account mAccount;
1035
Amith Yamasani04e0d262012-02-14 11:50:53 -08001036 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001037 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001038 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001039 true /* stripAuthTokenFromResult */, account.name,
1040 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001041 mFeatures = features;
1042 mAccount = account;
1043 }
1044
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001045 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001046 public void run() throws RemoteException {
1047 try {
1048 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1049 } catch (RemoteException e) {
1050 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1051 }
1052 }
1053
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001054 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001055 public void onResult(Bundle result) {
1056 IAccountManagerResponse response = getResponseAndClose();
1057 if (response != null) {
1058 try {
1059 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001060 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001061 return;
1062 }
Fred Quintana56285a62010-12-02 14:20:51 -08001063 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1064 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1065 + response);
1066 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001067 final Bundle newResult = new Bundle();
1068 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1069 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1070 response.onResult(newResult);
1071 } catch (RemoteException e) {
1072 // if the caller is dead then there is no one to care about remote exceptions
1073 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1074 Log.v(TAG, "failure while notifying response", e);
1075 }
1076 }
1077 }
1078 }
1079
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001080 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001081 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001082 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001083 + ", " + mAccount
1084 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1085 }
1086 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001087
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001088 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001089 public void renameAccount(
1090 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001091 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001092 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1093 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001094 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001095 + ", pid " + Binder.getCallingPid());
1096 }
1097 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001098 int userId = UserHandle.getCallingUserId();
1099 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001100 String msg = String.format(
1101 "uid %s cannot rename accounts of type: %s",
1102 callingUid,
1103 accountToRename.type);
1104 throw new SecurityException(msg);
1105 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001106 long identityToken = clearCallingIdentity();
1107 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001108 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001109 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001110 Bundle result = new Bundle();
1111 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1112 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1113 try {
1114 response.onResult(result);
1115 } catch (RemoteException e) {
1116 Log.w(TAG, e.getMessage());
1117 }
1118 } finally {
1119 restoreCallingIdentity(identityToken);
1120 }
1121 }
1122
1123 private Account renameAccountInternal(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001124 UserAccounts accounts, Account accountToRename, String newName) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001125 Account resultAccount = null;
1126 /*
1127 * Cancel existing notifications. Let authenticators
1128 * re-post notifications as required. But we don't know if
1129 * the authenticators have bound their notifications to
1130 * now stale account name data.
1131 *
1132 * With a rename api, we might not need to do this anymore but it
1133 * shouldn't hurt.
1134 */
1135 cancelNotification(
1136 getSigninRequiredNotificationId(accounts, accountToRename),
1137 new UserHandle(accounts.userId));
1138 synchronized(accounts.credentialsPermissionNotificationIds) {
1139 for (Pair<Pair<Account, String>, Integer> pair:
1140 accounts.credentialsPermissionNotificationIds.keySet()) {
1141 if (accountToRename.equals(pair.first.first)) {
1142 int id = accounts.credentialsPermissionNotificationIds.get(pair);
1143 cancelNotification(id, new UserHandle(accounts.userId));
1144 }
1145 }
1146 }
1147 synchronized (accounts.cacheLock) {
1148 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1149 db.beginTransaction();
1150 boolean isSuccessful = false;
1151 Account renamedAccount = new Account(newName, accountToRename.type);
1152 try {
1153 final ContentValues values = new ContentValues();
1154 values.put(ACCOUNTS_NAME, newName);
1155 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
1156 final long accountId = getAccountIdLocked(db, accountToRename);
1157 if (accountId >= 0) {
1158 final String[] argsAccountId = { String.valueOf(accountId) };
1159 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1160 db.setTransactionSuccessful();
1161 isSuccessful = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001162 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
1163 accounts);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001164 }
1165 } finally {
1166 db.endTransaction();
1167 if (isSuccessful) {
1168 /*
1169 * Database transaction was successful. Clean up cached
1170 * data associated with the account in the user profile.
1171 */
1172 insertAccountIntoCacheLocked(accounts, renamedAccount);
1173 /*
1174 * Extract the data and token caches before removing the
1175 * old account to preserve the user data associated with
1176 * the account.
1177 */
1178 HashMap<String, String> tmpData = accounts.userDataCache.get(accountToRename);
1179 HashMap<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
1180 removeAccountFromCacheLocked(accounts, accountToRename);
1181 /*
1182 * Update the cached data associated with the renamed
1183 * account.
1184 */
1185 accounts.userDataCache.put(renamedAccount, tmpData);
1186 accounts.authTokenCache.put(renamedAccount, tmpTokens);
1187 accounts.previousNameCache.put(
1188 renamedAccount,
1189 new AtomicReference<String>(accountToRename.name));
1190 resultAccount = renamedAccount;
1191
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001192 int parentUserId = accounts.userId;
1193 if (canHaveProfile(parentUserId)) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001194 /*
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001195 * Owner or system user account was renamed, rename the account for
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001196 * those users with which the account was shared.
1197 */
1198 List<UserInfo> users = mUserManager.getUsers(true);
1199 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001200 if (user.isRestricted()
1201 && (user.restrictedProfileParentId == parentUserId)) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001202 renameSharedAccountAsUser(accountToRename, newName, user.id);
1203 }
1204 }
1205 }
1206 sendAccountsChangedBroadcast(accounts.userId);
1207 }
1208 }
1209 }
1210 return resultAccount;
1211 }
1212
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001213 private boolean canHaveProfile(final int parentUserId) {
1214 final UserInfo userInfo = mUserManager.getUserInfo(parentUserId);
1215 return userInfo != null && userInfo.canHaveProfile();
1216 }
1217
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001218 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001219 public void removeAccount(IAccountManagerResponse response, Account account,
1220 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001221 removeAccountAsUser(
1222 response,
1223 account,
1224 expectActivityLaunch,
1225 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001226 }
1227
1228 @Override
1229 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001230 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001231 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001232 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1233 Log.v(TAG, "removeAccount: " + account
1234 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001235 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001236 + ", pid " + Binder.getCallingPid()
1237 + ", for user id " + userId);
1238 }
1239 if (response == null) throw new IllegalArgumentException("response is null");
1240 if (account == null) throw new IllegalArgumentException("account is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001241 // Only allow the system process to modify accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001242 if (isCrossUser(callingUid, userId)) {
1243 throw new SecurityException(
1244 String.format(
1245 "User %s tying remove account for %s" ,
1246 UserHandle.getCallingUserId(),
1247 userId));
1248 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001249 /*
1250 * Only the system or authenticator should be allowed to remove accounts for that
1251 * authenticator. This will let users remove accounts (via Settings in the system) but not
1252 * arbitrary applications (like competing authenticators).
1253 */
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001254 UserHandle user = new UserHandle(userId);
1255 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
1256 && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001257 String msg = String.format(
1258 "uid %s cannot remove accounts of type: %s",
1259 callingUid,
1260 account.type);
1261 throw new SecurityException(msg);
1262 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001263 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001264 try {
1265 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
1266 "User cannot modify accounts");
1267 } catch (RemoteException re) {
1268 }
1269 return;
1270 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001271 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001272 try {
1273 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
1274 "User cannot modify accounts of this type (policy).");
1275 } catch (RemoteException re) {
1276 }
1277 return;
1278 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001279 long identityToken = clearCallingIdentity();
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001280 UserAccounts accounts = getUserAccounts(userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01001281 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001282 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001283 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08001284 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001285 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001286 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001287 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08001288 }
1289 }
1290 }
1291
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001292 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
1293
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001294 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001295 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
1296 } finally {
1297 restoreCallingIdentity(identityToken);
1298 }
1299 }
1300
1301 @Override
1302 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001303 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001304 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1305 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001306 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001307 + ", pid " + Binder.getCallingPid());
1308 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001309 int userId = Binder.getCallingUserHandle().getIdentifier();
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001310 if (account == null) {
1311 /*
1312 * Null accounts should result in returning false, as per
1313 * AccountManage.addAccountExplicitly(...) java doc.
1314 */
1315 Log.e(TAG, "account is null");
1316 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001317 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001318 String msg = String.format(
1319 "uid %s cannot explicitly add accounts of type: %s",
1320 callingUid,
1321 account.type);
1322 throw new SecurityException(msg);
1323 }
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001324 UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001325 logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001326 long identityToken = clearCallingIdentity();
1327 try {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001328 return removeAccountInternal(accounts, account, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001329 } finally {
1330 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001331 }
Fred Quintana60307342009-03-24 22:48:12 -07001332 }
1333
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001334 private class RemoveAccountSession extends Session {
1335 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001336 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001337 Account account, boolean expectActivityLaunch) {
1338 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001339 true /* stripAuthTokenFromResult */, account.name,
1340 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001341 mAccount = account;
1342 }
1343
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001344 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001345 protected String toDebugString(long now) {
1346 return super.toDebugString(now) + ", removeAccount"
1347 + ", account " + mAccount;
1348 }
1349
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001350 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001351 public void run() throws RemoteException {
1352 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
1353 }
1354
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001355 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001356 public void onResult(Bundle result) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001357 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
1358 && !result.containsKey(AccountManager.KEY_INTENT)) {
1359 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001360 if (removalAllowed) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001361 removeAccountInternal(mAccounts, mAccount, getCallingUid());
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001362 }
1363 IAccountManagerResponse response = getResponseAndClose();
1364 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08001365 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1366 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1367 + response);
1368 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001369 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001370 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001371 try {
1372 response.onResult(result2);
1373 } catch (RemoteException e) {
1374 // ignore
1375 }
1376 }
1377 }
1378 super.onResult(result);
1379 }
1380 }
1381
Amith Yamasani04e0d262012-02-14 11:50:53 -08001382 /* For testing */
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001383 protected void removeAccountInternal(Account account) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001384 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001385 }
1386
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001387 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001388 int deleted;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001389 synchronized (accounts.cacheLock) {
1390 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001391 final long accountId = getAccountIdLocked(db, account);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001392 deleted = db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
1393 + "=?",
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001394 new String[]{account.name, account.type});
Amith Yamasani04e0d262012-02-14 11:50:53 -08001395 removeAccountFromCacheLocked(accounts, account);
1396 sendAccountsChangedBroadcast(accounts.userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001397
1398 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_ACCOUNTS, accountId, accounts);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001399 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001400 long id = Binder.clearCallingIdentity();
1401 try {
1402 int parentUserId = accounts.userId;
1403 if (canHaveProfile(parentUserId)) {
1404 // Remove from any restricted profiles that are sharing this account.
Amith Yamasani67df64b2012-12-14 12:09:36 -08001405 List<UserInfo> users = mUserManager.getUsers(true);
1406 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001407 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001408 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001409 }
1410 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001411 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001412 } finally {
1413 Binder.restoreCallingIdentity(id);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001414 }
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08001415 return (deleted > 0);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001416 }
1417
Maggie Benthalla12fccf2013-03-14 18:02:12 -04001418 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001419 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07001420 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001421 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1422 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07001423 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08001424 + ", pid " + Binder.getCallingPid());
1425 }
Fred Quintana382601f2010-03-25 12:25:10 -07001426 if (accountType == null) throw new IllegalArgumentException("accountType is null");
1427 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001428 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001429 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001430 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001431 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001432 synchronized (accounts.cacheLock) {
1433 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001434 db.beginTransaction();
1435 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001436 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001437 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001438 db.setTransactionSuccessful();
1439 } finally {
1440 db.endTransaction();
1441 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001442 }
Fred Quintana60307342009-03-24 22:48:12 -07001443 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001444 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001445 }
1446 }
1447
Carlos Valdivia91979be2015-05-22 14:11:35 -07001448 private void invalidateCustomTokenLocked(
1449 UserAccounts accounts,
1450 String accountType,
1451 String authToken) {
1452 if (authToken == null || accountType == null) {
1453 return;
1454 }
1455 // Also wipe out cached token in memory.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001456 accounts.accountTokenCaches.remove(accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001457 }
1458
Amith Yamasani04e0d262012-02-14 11:50:53 -08001459 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
1460 String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001461 if (authToken == null || accountType == null) {
1462 return;
1463 }
Fred Quintana33269202009-04-20 16:05:10 -07001464 Cursor cursor = db.rawQuery(
1465 "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
1466 + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
1467 + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
1468 + " FROM " + TABLE_ACCOUNTS
1469 + " JOIN " + TABLE_AUTHTOKENS
1470 + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
1471 + " = " + AUTHTOKENS_ACCOUNTS_ID
1472 + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
1473 + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
1474 new String[]{authToken, accountType});
1475 try {
1476 while (cursor.moveToNext()) {
1477 long authTokenId = cursor.getLong(0);
1478 String accountName = cursor.getString(1);
1479 String authTokenType = cursor.getString(2);
1480 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001481 writeAuthTokenIntoCacheLocked(
1482 accounts,
1483 db,
1484 new Account(accountName, accountType),
1485 authTokenType,
1486 null);
Fred Quintana60307342009-03-24 22:48:12 -07001487 }
Fred Quintana33269202009-04-20 16:05:10 -07001488 } finally {
1489 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07001490 }
1491 }
1492
Carlos Valdivia91979be2015-05-22 14:11:35 -07001493 private void saveCachedToken(
1494 UserAccounts accounts,
1495 Account account,
1496 String callerPkg,
1497 byte[] callerSigDigest,
1498 String tokenType,
1499 String token,
1500 long expiryMillis) {
1501
1502 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
1503 return;
1504 }
1505 cancelNotification(getSigninRequiredNotificationId(accounts, account),
1506 new UserHandle(accounts.userId));
1507 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001508 accounts.accountTokenCaches.put(
1509 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001510 }
1511 }
1512
Amith Yamasani04e0d262012-02-14 11:50:53 -08001513 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
1514 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07001515 if (account == null || type == null) {
1516 return false;
1517 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07001518 cancelNotification(getSigninRequiredNotificationId(accounts, account),
1519 new UserHandle(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08001520 synchronized (accounts.cacheLock) {
1521 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001522 db.beginTransaction();
1523 try {
1524 long accountId = getAccountIdLocked(db, account);
1525 if (accountId < 0) {
1526 return false;
1527 }
1528 db.delete(TABLE_AUTHTOKENS,
1529 AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
1530 new String[]{type});
1531 ContentValues values = new ContentValues();
1532 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
1533 values.put(AUTHTOKENS_TYPE, type);
1534 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
1535 if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
1536 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -08001537 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001538 return true;
1539 }
Fred Quintana33269202009-04-20 16:05:10 -07001540 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001541 } finally {
1542 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07001543 }
Fred Quintana60307342009-03-24 22:48:12 -07001544 }
1545 }
1546
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001547 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001548 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001549 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001550 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1551 Log.v(TAG, "peekAuthToken: " + account
1552 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001553 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001554 + ", pid " + Binder.getCallingPid());
1555 }
Fred Quintana382601f2010-03-25 12:25:10 -07001556 if (account == null) throw new IllegalArgumentException("account is null");
1557 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001558 int userId = UserHandle.getCallingUserId();
1559 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001560 String msg = String.format(
1561 "uid %s cannot peek the authtokens associated with accounts of type: %s",
1562 callingUid,
1563 account.type);
1564 throw new SecurityException(msg);
1565 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001566 long identityToken = clearCallingIdentity();
1567 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001568 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001569 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001570 } finally {
1571 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001572 }
Fred Quintana60307342009-03-24 22:48:12 -07001573 }
1574
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001575 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001576 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001577 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001578 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1579 Log.v(TAG, "setAuthToken: " + account
1580 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001581 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001582 + ", pid " + Binder.getCallingPid());
1583 }
Fred Quintana382601f2010-03-25 12:25:10 -07001584 if (account == null) throw new IllegalArgumentException("account is null");
1585 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001586 int userId = UserHandle.getCallingUserId();
1587 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001588 String msg = String.format(
1589 "uid %s cannot set auth tokens associated with accounts of type: %s",
1590 callingUid,
1591 account.type);
1592 throw new SecurityException(msg);
1593 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001594 long identityToken = clearCallingIdentity();
1595 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001596 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001597 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001598 } finally {
1599 restoreCallingIdentity(identityToken);
1600 }
Fred Quintana60307342009-03-24 22:48:12 -07001601 }
1602
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001603 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001604 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001605 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001606 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1607 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001608 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001609 + ", pid " + Binder.getCallingPid());
1610 }
Fred Quintana382601f2010-03-25 12:25:10 -07001611 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001612 int userId = UserHandle.getCallingUserId();
1613 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001614 String msg = String.format(
1615 "uid %s cannot set secrets for accounts of type: %s",
1616 callingUid,
1617 account.type);
1618 throw new SecurityException(msg);
1619 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001620 long identityToken = clearCallingIdentity();
1621 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001622 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001623 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001624 } finally {
1625 restoreCallingIdentity(identityToken);
1626 }
Fred Quintana60307342009-03-24 22:48:12 -07001627 }
1628
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001629 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
1630 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07001631 if (account == null) {
1632 return;
1633 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001634 synchronized (accounts.cacheLock) {
1635 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001636 db.beginTransaction();
1637 try {
1638 final ContentValues values = new ContentValues();
1639 values.put(ACCOUNTS_PASSWORD, password);
1640 final long accountId = getAccountIdLocked(db, account);
1641 if (accountId >= 0) {
1642 final String[] argsAccountId = {String.valueOf(accountId)};
1643 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
1644 db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001645 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001646 accounts.accountTokenCaches.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001647 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001648
1649 String action = (password == null || password.length() == 0) ?
1650 DebugDbHelper.ACTION_CLEAR_PASSWORD
1651 : DebugDbHelper.ACTION_SET_PASSWORD;
1652 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08001653 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001654 } finally {
1655 db.endTransaction();
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001656 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001657 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08001658 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001659 }
1660
Amith Yamasani04e0d262012-02-14 11:50:53 -08001661 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08001662 Log.i(TAG, "the accounts changed, sending broadcast of "
1663 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07001664 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07001665 }
1666
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001667 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001668 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001669 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001670 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1671 Log.v(TAG, "clearPassword: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001672 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001673 + ", pid " + Binder.getCallingPid());
1674 }
Fred Quintana382601f2010-03-25 12:25:10 -07001675 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001676 int userId = UserHandle.getCallingUserId();
1677 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001678 String msg = String.format(
1679 "uid %s cannot clear passwords for accounts of type: %s",
1680 callingUid,
1681 account.type);
1682 throw new SecurityException(msg);
1683 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001684 long identityToken = clearCallingIdentity();
1685 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001686 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001687 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001688 } finally {
1689 restoreCallingIdentity(identityToken);
1690 }
Fred Quintana60307342009-03-24 22:48:12 -07001691 }
1692
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001693 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001694 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001695 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001696 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1697 Log.v(TAG, "setUserData: " + account
1698 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001699 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001700 + ", pid " + Binder.getCallingPid());
1701 }
Fred Quintana382601f2010-03-25 12:25:10 -07001702 if (key == null) throw new IllegalArgumentException("key is null");
1703 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001704 int userId = UserHandle.getCallingUserId();
1705 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001706 String msg = String.format(
1707 "uid %s cannot set user data for accounts of type: %s",
1708 callingUid,
1709 account.type);
1710 throw new SecurityException(msg);
1711 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001712 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001713 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001714 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001715 setUserdataInternal(accounts, account, key, value);
Fred Quintana60307342009-03-24 22:48:12 -07001716 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001717 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001718 }
1719 }
1720
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001721 private void setUserdataInternal(UserAccounts accounts, Account account, String key,
Amith Yamasani04e0d262012-02-14 11:50:53 -08001722 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07001723 if (account == null || key == null) {
1724 return;
1725 }
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001726 synchronized (accounts.cacheLock) {
1727 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1728 db.beginTransaction();
1729 try {
1730 long accountId = getAccountIdLocked(db, account);
1731 if (accountId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001732 return;
1733 }
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001734 long extrasId = getExtrasIdLocked(db, accountId, key);
1735 if (extrasId < 0 ) {
1736 extrasId = insertExtraLocked(db, accountId, key, value);
1737 if (extrasId < 0) {
1738 return;
1739 }
1740 } else {
1741 ContentValues values = new ContentValues();
1742 values.put(EXTRAS_VALUE, value);
1743 if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
1744 return;
1745 }
Simranjit Singh Kohlia666d742015-08-07 13:34:09 -07001746
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00001747 }
1748 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
1749 db.setTransactionSuccessful();
1750 } finally {
1751 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001752 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001753 }
1754 }
1755
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001756 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08001757 if (result == null) {
1758 Log.e(TAG, "the result is unexpectedly null", new Exception());
1759 }
1760 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1761 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1762 + response);
1763 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001764 try {
1765 response.onResult(result);
1766 } catch (RemoteException e) {
1767 // if the caller is dead then there is no one to care about remote
1768 // exceptions
1769 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1770 Log.v(TAG, "failure while notifying response", e);
1771 }
1772 }
1773 }
1774
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001775 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07001776 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
1777 final String authTokenType)
1778 throws RemoteException {
1779 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08001780 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
1781
Fred Quintanad9640ec2012-05-23 12:37:00 -07001782 final int callingUid = getCallingUid();
1783 clearCallingIdentity();
Amith Yamasani27db4682013-03-30 17:07:47 -07001784 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07001785 throw new SecurityException("can only call from system");
1786 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001787 int userId = UserHandle.getUserId(callingUid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08001788 long identityToken = clearCallingIdentity();
1789 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001790 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001791 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
1792 false /* stripAuthTokenFromResult */, null /* accountName */,
1793 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001794 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001795 protected String toDebugString(long now) {
1796 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07001797 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08001798 + ", authTokenType " + authTokenType;
1799 }
1800
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001801 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001802 public void run() throws RemoteException {
1803 mAuthenticator.getAuthTokenLabel(this, authTokenType);
1804 }
1805
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001806 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08001807 public void onResult(Bundle result) {
1808 if (result != null) {
1809 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
1810 Bundle bundle = new Bundle();
1811 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
1812 super.onResult(bundle);
1813 return;
1814 } else {
1815 super.onResult(result);
1816 }
1817 }
1818 }.bind();
1819 } finally {
1820 restoreCallingIdentity(identityToken);
1821 }
1822 }
1823
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001824 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07001825 public void getAuthToken(
1826 IAccountManagerResponse response,
1827 final Account account,
1828 final String authTokenType,
1829 final boolean notifyOnAuthFailure,
1830 final boolean expectActivityLaunch,
1831 final Bundle loginOptions) {
Fred Quintana56285a62010-12-02 14:20:51 -08001832 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1833 Log.v(TAG, "getAuthToken: " + account
1834 + ", response " + response
1835 + ", authTokenType " + authTokenType
1836 + ", notifyOnAuthFailure " + notifyOnAuthFailure
1837 + ", expectActivityLaunch " + expectActivityLaunch
1838 + ", caller's uid " + Binder.getCallingUid()
1839 + ", pid " + Binder.getCallingPid());
1840 }
Fred Quintana382601f2010-03-25 12:25:10 -07001841 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001842 try {
1843 if (account == null) {
1844 Slog.w(TAG, "getAuthToken called with null account");
1845 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
1846 return;
1847 }
1848 if (authTokenType == null) {
1849 Slog.w(TAG, "getAuthToken called with null authTokenType");
1850 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
1851 return;
1852 }
1853 } catch (RemoteException e) {
1854 Slog.w(TAG, "Failed to report error back to the client." + e);
1855 return;
1856 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001857 int userId = UserHandle.getCallingUserId();
1858 long ident = Binder.clearCallingIdentity();
1859 final UserAccounts accounts;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001860 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001861 try {
1862 accounts = getUserAccounts(userId);
1863 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
1864 AuthenticatorDescription.newKey(account.type), accounts.userId);
1865 } finally {
1866 Binder.restoreCallingIdentity(ident);
1867 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07001868
Costin Manolachea40c6302010-12-13 14:50:45 -08001869 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07001870 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08001871
1872 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001873 final int callerUid = Binder.getCallingUid();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001874 final boolean permissionGranted =
1875 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08001876
Carlos Valdivia91979be2015-05-22 14:11:35 -07001877 // Get the calling package. We will use it for the purpose of caching.
1878 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07001879 List<String> callerOwnedPackageNames;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001880 ident = Binder.clearCallingIdentity();
Amith Yamasanie7360012015-06-03 17:39:40 -07001881 try {
1882 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
1883 } finally {
1884 Binder.restoreCallingIdentity(ident);
1885 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07001886 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
1887 String msg = String.format(
1888 "Uid %s is attempting to illegally masquerade as package %s!",
1889 callerUid,
1890 callerPkg);
1891 throw new SecurityException(msg);
1892 }
1893
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001894 // let authenticator know the identity of the caller
1895 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
1896 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07001897
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07001898 if (notifyOnAuthFailure) {
1899 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08001900 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001901
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001902 long identityToken = clearCallingIdentity();
1903 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07001904 // Distill the caller's package signatures into a single digest.
1905 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
1906
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001907 // if the caller has permission, do the peek. otherwise go the more expensive
1908 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08001909 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001910 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001911 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001912 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001913 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
1914 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1915 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001916 onResult(response, result);
1917 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07001918 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001919 }
1920
Carlos Valdivia91979be2015-05-22 14:11:35 -07001921 if (customTokens) {
1922 /*
1923 * Look up tokens in the new cache only if the loginOptions don't have parameters
1924 * outside of those expected to be injected by the AccountManager, e.g.
1925 * ANDORID_PACKAGE_NAME.
1926 */
1927 String token = readCachedTokenInternal(
1928 accounts,
1929 account,
1930 authTokenType,
1931 callerPkg,
1932 callerPkgSigDigest);
1933 if (token != null) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001934 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1935 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
1936 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07001937 Bundle result = new Bundle();
1938 result.putString(AccountManager.KEY_AUTHTOKEN, token);
1939 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
1940 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
1941 onResult(response, result);
1942 return;
1943 }
1944 }
1945
Amith Yamasani04e0d262012-02-14 11:50:53 -08001946 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001947 false /* stripAuthTokenFromResult */, account.name,
1948 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001949 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001950 protected String toDebugString(long now) {
1951 if (loginOptions != null) loginOptions.keySet();
1952 return super.toDebugString(now) + ", getAuthToken"
1953 + ", " + account
1954 + ", authTokenType " + authTokenType
1955 + ", loginOptions " + loginOptions
1956 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
1957 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001958
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001959 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001960 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001961 // If the caller doesn't have permission then create and return the
1962 // "grant permission" intent instead of the "getAuthToken" intent.
1963 if (!permissionGranted) {
1964 mAuthenticator.getAuthTokenLabel(this, authTokenType);
1965 } else {
1966 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
1967 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001968 }
1969
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001970 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001971 public void onResult(Bundle result) {
1972 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001973 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001974 Intent intent = newGrantCredentialsPermissionIntent(
1975 account,
1976 callerUid,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001977 new AccountAuthenticatorResponse(this),
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001978 authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001979 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001980 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001981 onResult(bundle);
1982 return;
1983 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001984 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001985 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001986 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
1987 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001988 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07001989 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001990 "the type and name should not be empty");
1991 return;
1992 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07001993 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08001994 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07001995 saveAuthTokenToDatabase(
1996 mAccounts,
1997 resultAccount,
1998 authTokenType,
1999 authToken);
2000 }
2001 long expiryMillis = result.getLong(
2002 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2003 if (customTokens
2004 && expiryMillis > System.currentTimeMillis()) {
2005 saveCachedToken(
2006 mAccounts,
2007 account,
2008 callerPkg,
2009 callerPkgSigDigest,
2010 authTokenType,
2011 authToken,
2012 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08002013 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002014 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002015
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002016 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08002017 if (intent != null && notifyOnAuthFailure && !customTokens) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002018 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002019 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Dianne Hackborn41203752012-08-31 14:05:51 -07002020 intent, accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002021 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002022 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002023 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07002024 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002025 }.bind();
2026 } finally {
2027 restoreCallingIdentity(identityToken);
2028 }
Fred Quintana60307342009-03-24 22:48:12 -07002029 }
2030
Carlos Valdivia91979be2015-05-22 14:11:35 -07002031 private byte[] calculatePackageSignatureDigest(String callerPkg) {
2032 MessageDigest digester;
2033 try {
2034 digester = MessageDigest.getInstance("SHA-256");
2035 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2036 callerPkg, PackageManager.GET_SIGNATURES);
2037 for (Signature sig : pkgInfo.signatures) {
2038 digester.update(sig.toByteArray());
2039 }
2040 } catch (NoSuchAlgorithmException x) {
2041 Log.wtf(TAG, "SHA-256 should be available", x);
2042 digester = null;
2043 } catch (NameNotFoundException e) {
2044 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2045 digester = null;
2046 }
2047 return (digester == null) ? null : digester.digest();
2048 }
2049
Dianne Hackborn41203752012-08-31 14:05:51 -07002050 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
2051 int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002052 int uid = intent.getIntExtra(
2053 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2054 String authTokenType = intent.getStringExtra(
2055 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
Eric Fischeree452ee2009-08-31 17:58:06 -07002056 final String titleAndSubtitle =
2057 mContext.getString(R.string.permission_request_notification_with_subtitle,
2058 account.name);
2059 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07002060 String title = titleAndSubtitle;
2061 String subtitle = "";
2062 if (index > 0) {
2063 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002064 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07002065 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002066 UserHandle user = new UserHandle(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01002067 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04002068 Notification n = new Notification.Builder(contextForUser)
2069 .setSmallIcon(android.R.drawable.stat_sys_warning)
2070 .setWhen(0)
2071 .setColor(contextForUser.getColor(
2072 com.android.internal.R.color.system_notification_accent_color))
2073 .setContentTitle(title)
2074 .setContentText(subtitle)
2075 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
2076 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
2077 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002078 installNotification(getCredentialPermissionNotificationId(
2079 account, authTokenType, uid), n, user);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002080 }
2081
Costin Manolache5f383ad92010-12-02 16:44:46 -08002082 private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002083 AccountAuthenticatorResponse response, String authTokenType) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002084
2085 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Brian Carlstrom46703b02011-04-06 15:41:29 -07002086 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
Costin Manolache9ec17362011-01-17 12:12:37 -08002087 // Since it was set in Eclair+ we can't change it without breaking apps using
2088 // the intent from a non-Activity context.
2089 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002090 intent.addCategory(
2091 String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
Costin Manolache5f383ad92010-12-02 16:44:46 -08002092
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002093 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002094 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
2095 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002096 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002097
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002098 return intent;
2099 }
2100
2101 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
2102 int uid) {
2103 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07002104 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002105 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002106 final Pair<Pair<Account, String>, Integer> key =
2107 new Pair<Pair<Account, String>, Integer>(
2108 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002109 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002110 if (id == null) {
2111 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002112 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002113 }
2114 }
2115 return id;
2116 }
2117
Amith Yamasani04e0d262012-02-14 11:50:53 -08002118 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002119 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002120 synchronized (accounts.signinRequiredNotificationIds) {
2121 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002122 if (id == null) {
2123 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002124 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002125 }
2126 }
2127 return id;
2128 }
2129
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002130 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07002131 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07002132 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002133 final boolean expectActivityLaunch, final Bundle optionsIn) {
Fred Quintana56285a62010-12-02 14:20:51 -08002134 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2135 Log.v(TAG, "addAccount: accountType " + accountType
2136 + ", response " + response
2137 + ", authTokenType " + authTokenType
2138 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2139 + ", expectActivityLaunch " + expectActivityLaunch
2140 + ", caller's uid " + Binder.getCallingUid()
2141 + ", pid " + Binder.getCallingPid());
2142 }
Fred Quintana382601f2010-03-25 12:25:10 -07002143 if (response == null) throw new IllegalArgumentException("response is null");
2144 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002145
Amith Yamasani71e6c692013-03-24 17:39:28 -07002146 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002147 final int uid = Binder.getCallingUid();
2148 final int userId = UserHandle.getUserId(uid);
2149 if (!canUserModifyAccounts(userId, uid)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002150 try {
2151 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2152 "User is not allowed to add an account!");
2153 } catch (RemoteException re) {
2154 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002155 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002156 return;
2157 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002158 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002159 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002160 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2161 "User cannot modify accounts of this type (policy).");
2162 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07002163 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002164 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2165 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002166 return;
2167 }
2168
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002169 final int pid = Binder.getCallingPid();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002170 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2171 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2172 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2173
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002174 int usrId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002175 long identityToken = clearCallingIdentity();
2176 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002177 UserAccounts accounts = getUserAccounts(usrId);
2178 logRecordWithUid(
2179 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002180 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002181 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002182 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002183 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002184 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07002185 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07002186 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002187 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002188
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002189 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002190 protected String toDebugString(long now) {
2191 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07002192 + ", accountType " + accountType
2193 + ", requiredFeatures "
2194 + (requiredFeatures != null
2195 ? TextUtils.join(",", requiredFeatures)
2196 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002197 }
2198 }.bind();
2199 } finally {
2200 restoreCallingIdentity(identityToken);
2201 }
Fred Quintana60307342009-03-24 22:48:12 -07002202 }
2203
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002204 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002205 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
2206 final String authTokenType, final String[] requiredFeatures,
2207 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002208 int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002209 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2210 Log.v(TAG, "addAccount: accountType " + accountType
2211 + ", response " + response
2212 + ", authTokenType " + authTokenType
2213 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2214 + ", expectActivityLaunch " + expectActivityLaunch
2215 + ", caller's uid " + Binder.getCallingUid()
2216 + ", pid " + Binder.getCallingPid()
2217 + ", for user id " + userId);
2218 }
2219 if (response == null) throw new IllegalArgumentException("response is null");
2220 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002221 // Only allow the system process to add accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002222 if (isCrossUser(callingUid, userId)) {
2223 throw new SecurityException(
2224 String.format(
2225 "User %s trying to add account for %s" ,
2226 UserHandle.getCallingUserId(),
2227 userId));
2228 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002229
2230 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002231 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002232 try {
2233 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2234 "User is not allowed to add an account!");
2235 } catch (RemoteException re) {
2236 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002237 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002238 return;
2239 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002240 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002241 try {
2242 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2243 "User cannot modify accounts of this type (policy).");
2244 } catch (RemoteException re) {
2245 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002246 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2247 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002248 return;
2249 }
2250
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002251 final int pid = Binder.getCallingPid();
2252 final int uid = Binder.getCallingUid();
2253 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2254 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2255 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2256
2257 long identityToken = clearCallingIdentity();
2258 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002259 UserAccounts accounts = getUserAccounts(userId);
2260 logRecordWithUid(
2261 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002262 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002263 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07002264 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002265 @Override
2266 public void run() throws RemoteException {
2267 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
2268 options);
2269 }
2270
2271 @Override
2272 protected String toDebugString(long now) {
2273 return super.toDebugString(now) + ", addAccount"
2274 + ", accountType " + accountType
2275 + ", requiredFeatures "
2276 + (requiredFeatures != null
2277 ? TextUtils.join(",", requiredFeatures)
2278 : null);
2279 }
2280 }.bind();
2281 } finally {
2282 restoreCallingIdentity(identityToken);
2283 }
2284 }
2285
Sandra Kwan78812282015-11-04 11:19:47 -08002286 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08002287 public void startAddAccountSession(
2288 final IAccountManagerResponse response,
2289 final String accountType,
2290 final String authTokenType,
2291 final String[] requiredFeatures,
Sandra Kwan78812282015-11-04 11:19:47 -08002292 final boolean expectActivityLaunch,
2293 final Bundle optionsIn) {
2294 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2295 Log.v(TAG,
2296 "startAddAccountSession: accountType " + accountType
2297 + ", response " + response
2298 + ", authTokenType " + authTokenType
2299 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
2300 + ", expectActivityLaunch " + expectActivityLaunch
2301 + ", caller's uid " + Binder.getCallingUid()
2302 + ", pid " + Binder.getCallingPid());
2303 }
2304 if (response == null) {
2305 throw new IllegalArgumentException("response is null");
2306 }
2307 if (accountType == null) {
2308 throw new IllegalArgumentException("accountType is null");
2309 }
2310
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002311 final int uid = Binder.getCallingUid();
Sandra Kwana578d112015-12-16 16:01:43 -08002312 // Only allow system to start session
2313 if (!isSystemUid(uid)) {
2314 String msg = String.format(
2315 "uid %s cannot stat add account session.",
2316 uid);
2317 throw new SecurityException(msg);
2318 }
2319
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002320 final int userId = UserHandle.getUserId(uid);
2321 if (!canUserModifyAccounts(userId, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08002322 try {
2323 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2324 "User is not allowed to add an account!");
2325 } catch (RemoteException re) {
2326 }
2327 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2328 return;
2329 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002330 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08002331 try {
2332 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2333 "User cannot modify accounts of this type (policy).");
2334 } catch (RemoteException re) {
2335 }
2336 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2337 userId);
2338 return;
2339 }
2340
2341 final int pid = Binder.getCallingPid();
Sandra Kwan78812282015-11-04 11:19:47 -08002342 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
2343 options.putInt(AccountManager.KEY_CALLER_UID, uid);
2344 options.putInt(AccountManager.KEY_CALLER_PID, pid);
2345
2346 int usrId = UserHandle.getCallingUserId();
2347 long identityToken = clearCallingIdentity();
2348 try {
2349 UserAccounts accounts = getUserAccounts(usrId);
2350 logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
2351 TABLE_ACCOUNTS, uid);
2352 new StartAccountSession(accounts, response, accountType, expectActivityLaunch,
2353 null /* accountName */, false /* authDetailsRequired */,
2354 true /* updateLastAuthenticationTime */) {
2355 @Override
2356 public void run() throws RemoteException {
2357 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
2358 requiredFeatures, options);
2359 }
2360
2361 @Override
2362 protected String toDebugString(long now) {
2363 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
2364 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
2365 + accountType + ", requiredFeatures "
2366 + (requiredFeatures != null ? requiredFeaturesStr : null);
2367 }
2368 }.bind();
2369 } finally {
2370 restoreCallingIdentity(identityToken);
2371 }
2372 }
2373
2374 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
2375 private abstract class StartAccountSession extends Session {
2376
2377 public StartAccountSession(UserAccounts accounts, IAccountManagerResponse response,
2378 String accountType, boolean expectActivityLaunch, String accountName,
2379 boolean authDetailsRequired, boolean updateLastAuthenticationTime) {
2380 super(accounts, response, accountType, expectActivityLaunch,
2381 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
2382 updateLastAuthenticationTime);
2383 }
2384
2385 @Override
2386 public void onResult(Bundle result) {
2387 mNumResults++;
2388 Intent intent = null;
2389
2390 if (result != null
2391 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
2392 /*
2393 * The Authenticator API allows third party authenticators to
2394 * supply arbitrary intents to other apps that they can run,
2395 * this can be very bad when those apps are in the system like
2396 * the System Settings.
2397 */
2398 int authenticatorUid = Binder.getCallingUid();
2399 long bid = Binder.clearCallingIdentity();
2400 try {
2401 PackageManager pm = mContext.getPackageManager();
2402 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
2403 int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
2404 if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid,
2405 targetUid)) {
2406 throw new SecurityException("Activity to be started with KEY_INTENT must "
2407 + "share Authenticator's signatures");
2408 }
2409 } finally {
2410 Binder.restoreCallingIdentity(bid);
2411 }
2412 }
2413
2414 IAccountManagerResponse response;
2415 if (mExpectActivityLaunch && result != null
2416 && result.containsKey(AccountManager.KEY_INTENT)) {
2417 response = mResponse;
2418 } else {
2419 response = getResponseAndClose();
2420 }
2421 if (response == null) {
2422 return;
2423 }
2424 if (result == null) {
2425 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2426 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
2427 + response);
2428 }
2429 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2430 "null bundle returned");
2431 return;
2432 }
2433
2434 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
2435 // All AccountManager error codes are greater
2436 // than 0
2437 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
2438 result.getString(AccountManager.KEY_ERROR_MESSAGE));
2439 return;
2440 }
2441
2442 // Strip auth token from result.
2443 result.remove(AccountManager.KEY_AUTHTOKEN);
2444
2445 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2446 Log.v(TAG,
2447 getClass().getSimpleName() + " calling onResult() on response " + response);
2448 }
2449
2450 // Get the session bundle created by authenticator. The
2451 // bundle contains data necessary for finishing the session
2452 // later. The session bundle will be encrypted here and
2453 // decrypted later when trying to finish the session.
2454 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
2455 if (sessionBundle != null) {
2456 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2457 if (TextUtils.isEmpty(accountType)
Andreas Gampe9b041742015-12-11 17:23:33 -08002458 || !mAccountType.equalsIgnoreCase(accountType)) {
Sandra Kwan78812282015-11-04 11:19:47 -08002459 Log.w(TAG, "Account type in session bundle doesn't match request.");
2460 }
2461 // Add accountType info to session bundle. This will
2462 // override any value set by authenticator.
2463 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
2464
2465 // Encrypt session bundle before returning to caller.
2466 try {
2467 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2468 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
2469 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
2470 } catch (GeneralSecurityException e) {
2471 if (Log.isLoggable(TAG, Log.DEBUG)) {
2472 Log.v(TAG, "Failed to encrypt session bundle!", e);
2473 }
2474 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
2475 "failed to encrypt session bundle");
2476 return;
2477 }
2478 }
2479
2480 sendResponse(response, result);
2481 }
2482 }
2483
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002484 @Override
Sandra Kwan0b84b452016-01-20 15:25:42 -08002485 public void finishSessionAsUser(IAccountManagerResponse response,
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002486 @NonNull Bundle sessionBundle,
2487 boolean expectActivityLaunch,
Sandra Kwan0b84b452016-01-20 15:25:42 -08002488 Bundle appInfo,
2489 int userId) {
2490 int callingUid = Binder.getCallingUid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002491 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2492 Log.v(TAG,
Sandra Kwan0b84b452016-01-20 15:25:42 -08002493 "finishSession: response "+ response
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002494 + ", expectActivityLaunch " + expectActivityLaunch
Sandra Kwan0b84b452016-01-20 15:25:42 -08002495 + ", caller's uid " + callingUid
2496 + ", caller's user id " + UserHandle.getCallingUserId()
2497 + ", pid " + Binder.getCallingPid()
2498 + ", for user id " + userId);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002499 }
2500 if (response == null) {
2501 throw new IllegalArgumentException("response is null");
2502 }
2503
2504 // Session bundle is the encrypted bundle of the original bundle created by authenticator.
2505 // Account type is added to it before encryption.
2506 if (sessionBundle == null || sessionBundle.size() == 0) {
2507 throw new IllegalArgumentException("sessionBundle is empty");
2508 }
2509
Sandra Kwan0b84b452016-01-20 15:25:42 -08002510 // Only allow the system process to finish session for other users
2511 if (isCrossUser(callingUid, userId)) {
2512 throw new SecurityException(
2513 String.format(
2514 "User %s trying to finish session for %s without cross user permission",
2515 UserHandle.getCallingUserId(),
2516 userId));
2517 }
2518
Sandra Kwana578d112015-12-16 16:01:43 -08002519 // Only allow system to finish session
Sandra Kwan0b84b452016-01-20 15:25:42 -08002520 if (!isSystemUid(callingUid)) {
Sandra Kwana578d112015-12-16 16:01:43 -08002521 String msg = String.format(
Sandra Kwan0b84b452016-01-20 15:25:42 -08002522 "uid %s cannot finish session because it's not system uid.",
2523 callingUid);
Sandra Kwana578d112015-12-16 16:01:43 -08002524 throw new SecurityException(msg);
2525 }
2526
Sandra Kwan0b84b452016-01-20 15:25:42 -08002527 if (!canUserModifyAccounts(userId, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002528 sendErrorResponse(response,
2529 AccountManager.ERROR_CODE_USER_RESTRICTED,
2530 "User is not allowed to add an account!");
2531 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
2532 return;
2533 }
2534
2535 final int pid = Binder.getCallingPid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002536 final Bundle decryptedBundle;
2537 final String accountType;
2538 // First decrypt session bundle to get account type for checking permission.
2539 try {
2540 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
2541 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
2542 if (decryptedBundle == null) {
2543 sendErrorResponse(
2544 response,
2545 AccountManager.ERROR_CODE_BAD_REQUEST,
2546 "failed to decrypt session bundle");
2547 return;
2548 }
2549 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
2550 // Account type cannot be null. This should not happen if session bundle was created
2551 // properly by #StartAccountSession.
2552 if (TextUtils.isEmpty(accountType)) {
2553 sendErrorResponse(
2554 response,
2555 AccountManager.ERROR_CODE_BAD_ARGUMENTS,
2556 "accountType is empty");
2557 return;
2558 }
2559
2560 // If by any chances, decryptedBundle contains colliding keys with
2561 // system info
2562 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
2563 // update credentials flow, we should replace with the new values of the current call.
2564 if (appInfo != null) {
2565 decryptedBundle.putAll(appInfo);
2566 }
2567
2568 // Add info that may be used by add account or update credentials flow.
Sandra Kwan0b84b452016-01-20 15:25:42 -08002569 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002570 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
2571 } catch (GeneralSecurityException e) {
2572 if (Log.isLoggable(TAG, Log.DEBUG)) {
2573 Log.v(TAG, "Failed to decrypt session bundle!", e);
2574 }
2575 sendErrorResponse(
2576 response,
2577 AccountManager.ERROR_CODE_BAD_REQUEST,
2578 "failed to decrypt session bundle");
2579 return;
2580 }
2581
Sandra Kwan0b84b452016-01-20 15:25:42 -08002582 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002583 sendErrorResponse(
2584 response,
2585 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2586 "User cannot modify accounts of this type (policy).");
2587 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2588 userId);
2589 return;
2590 }
2591
2592 long identityToken = clearCallingIdentity();
2593 try {
2594 UserAccounts accounts = getUserAccounts(userId);
2595 logRecordWithUid(
2596 accounts,
2597 DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
2598 TABLE_ACCOUNTS,
Sandra Kwan0b84b452016-01-20 15:25:42 -08002599 callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08002600 new Session(
2601 accounts,
2602 response,
2603 accountType,
2604 expectActivityLaunch,
2605 true /* stripAuthTokenFromResult */,
2606 null /* accountName */,
2607 false /* authDetailsRequired */,
2608 true /* updateLastAuthenticationTime */) {
2609 @Override
2610 public void run() throws RemoteException {
2611 mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
2612 }
2613
2614 @Override
2615 protected String toDebugString(long now) {
2616 return super.toDebugString(now)
2617 + ", finishSession"
2618 + ", accountType " + accountType;
2619 }
2620 }.bind();
2621 } finally {
2622 restoreCallingIdentity(identityToken);
2623 }
2624 }
2625
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002626 private void showCantAddAccount(int errorCode, int userId) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002627 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
2628 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
2629 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2630 long identityToken = clearCallingIdentity();
2631 try {
Amith Yamasaniae7034a2014-09-22 12:42:12 -07002632 mContext.startActivityAsUser(cantAddAccount, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002633 } finally {
2634 restoreCallingIdentity(identityToken);
2635 }
2636 }
2637
2638 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002639 public void confirmCredentialsAsUser(
2640 IAccountManagerResponse response,
2641 final Account account,
2642 final Bundle options,
2643 final boolean expectActivityLaunch,
Amith Yamasani2c7bc262012-11-05 16:46:02 -08002644 int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002645 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002646 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2647 Log.v(TAG, "confirmCredentials: " + account
2648 + ", response " + response
2649 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002650 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002651 + ", pid " + Binder.getCallingPid());
2652 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002653 // Only allow the system process to read accounts of other users
2654 if (isCrossUser(callingUid, userId)) {
2655 throw new SecurityException(
2656 String.format(
2657 "User %s trying to confirm account credentials for %s" ,
2658 UserHandle.getCallingUserId(),
2659 userId));
2660 }
Fred Quintana382601f2010-03-25 12:25:10 -07002661 if (response == null) throw new IllegalArgumentException("response is null");
2662 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002663 long identityToken = clearCallingIdentity();
2664 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002665 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002666 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002667 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002668 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002669 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002670 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002671 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002672 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002673 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002674 protected String toDebugString(long now) {
2675 return super.toDebugString(now) + ", confirmCredentials"
2676 + ", " + account;
2677 }
2678 }.bind();
2679 } finally {
2680 restoreCallingIdentity(identityToken);
2681 }
Fred Quintana60307342009-03-24 22:48:12 -07002682 }
2683
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002684 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002685 public void updateCredentials(IAccountManagerResponse response, final Account account,
2686 final String authTokenType, final boolean expectActivityLaunch,
2687 final Bundle loginOptions) {
Fred Quintana56285a62010-12-02 14:20:51 -08002688 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2689 Log.v(TAG, "updateCredentials: " + account
2690 + ", response " + response
2691 + ", authTokenType " + authTokenType
2692 + ", expectActivityLaunch " + expectActivityLaunch
2693 + ", caller's uid " + Binder.getCallingUid()
2694 + ", pid " + Binder.getCallingPid());
2695 }
Fred Quintana382601f2010-03-25 12:25:10 -07002696 if (response == null) throw new IllegalArgumentException("response is null");
2697 if (account == null) throw new IllegalArgumentException("account is null");
2698 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002699 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002700 long identityToken = clearCallingIdentity();
2701 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002702 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002703 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002704 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07002705 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002706 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002707 public void run() throws RemoteException {
2708 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
2709 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002710 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002711 protected String toDebugString(long now) {
2712 if (loginOptions != null) loginOptions.keySet();
2713 return super.toDebugString(now) + ", updateCredentials"
2714 + ", " + account
2715 + ", authTokenType " + authTokenType
2716 + ", loginOptions " + loginOptions;
2717 }
2718 }.bind();
2719 } finally {
2720 restoreCallingIdentity(identityToken);
2721 }
Fred Quintana60307342009-03-24 22:48:12 -07002722 }
2723
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002724 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08002725 public void startUpdateCredentialsSession(
2726 IAccountManagerResponse response,
2727 final Account account,
2728 final String authTokenType,
2729 final boolean expectActivityLaunch,
2730 final Bundle loginOptions) {
2731 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2732 Log.v(TAG,
2733 "startUpdateCredentialsSession: " + account + ", response " + response
2734 + ", authTokenType " + authTokenType + ", expectActivityLaunch "
2735 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
2736 + ", pid " + Binder.getCallingPid());
2737 }
2738 if (response == null) {
2739 throw new IllegalArgumentException("response is null");
2740 }
2741 if (account == null) {
2742 throw new IllegalArgumentException("account is null");
2743 }
Sandra Kwana578d112015-12-16 16:01:43 -08002744
2745 final int uid = Binder.getCallingUid();
2746 // Only allow system to start session
2747 if (!isSystemUid(uid)) {
2748 String msg = String.format(
2749 "uid %s cannot start update credentials session.",
2750 uid);
2751 throw new SecurityException(msg);
2752 }
2753
Sandra Kwane68c37e2015-11-12 17:11:49 -08002754 int userId = UserHandle.getCallingUserId();
2755 long identityToken = clearCallingIdentity();
2756 try {
2757 UserAccounts accounts = getUserAccounts(userId);
2758 new StartAccountSession(
2759 accounts,
2760 response,
2761 account.type,
2762 expectActivityLaunch,
2763 account.name,
2764 false /* authDetailsRequired */,
2765 true /* updateLastCredentialTime */) {
2766 @Override
2767 public void run() throws RemoteException {
2768 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
2769 loginOptions);
2770 }
2771
2772 @Override
2773 protected String toDebugString(long now) {
2774 if (loginOptions != null)
2775 loginOptions.keySet();
2776 return super.toDebugString(now)
2777 + ", startUpdateCredentialsSession"
2778 + ", " + account
2779 + ", authTokenType " + authTokenType
2780 + ", loginOptions " + loginOptions;
2781 }
2782 }.bind();
2783 } finally {
2784 restoreCallingIdentity(identityToken);
2785 }
2786 }
2787
2788 @Override
Sandra Kwan390c9d22016-01-12 14:13:37 -08002789 public void isCredentialsUpdateSuggested(
2790 IAccountManagerResponse response,
2791 final Account account,
2792 final String statusToken) {
2793 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2794 Log.v(TAG,
2795 "isCredentialsUpdateSuggested: " + account + ", response " + response
2796 + ", caller's uid " + Binder.getCallingUid()
2797 + ", pid " + Binder.getCallingPid());
2798 }
2799 if (response == null) {
2800 throw new IllegalArgumentException("response is null");
2801 }
2802 if (account == null) {
2803 throw new IllegalArgumentException("account is null");
2804 }
2805 if (TextUtils.isEmpty(statusToken)) {
2806 throw new IllegalArgumentException("status token is empty");
2807 }
2808
2809 int uid = Binder.getCallingUid();
2810 // Only allow system to start session
2811 if (!isSystemUid(uid)) {
2812 String msg = String.format(
2813 "uid %s cannot stat add account session.",
2814 uid);
2815 throw new SecurityException(msg);
2816 }
2817
2818 int usrId = UserHandle.getCallingUserId();
2819 long identityToken = clearCallingIdentity();
2820 try {
2821 UserAccounts accounts = getUserAccounts(usrId);
2822 new Session(accounts, response, account.type, false /* expectActivityLaunch */,
2823 false /* stripAuthTokenFromResult */, account.name,
2824 false /* authDetailsRequired */) {
2825 @Override
2826 protected String toDebugString(long now) {
2827 return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
2828 + ", " + account;
2829 }
2830
2831 @Override
2832 public void run() throws RemoteException {
2833 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
2834 }
2835
2836 @Override
2837 public void onResult(Bundle result) {
2838 IAccountManagerResponse response = getResponseAndClose();
2839 if (response == null) {
2840 return;
2841 }
2842
2843 if (result == null) {
2844 sendErrorResponse(
2845 response,
2846 AccountManager.ERROR_CODE_INVALID_RESPONSE,
2847 "null bundle");
2848 return;
2849 }
2850
2851 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2852 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2853 + response);
2854 }
2855 // Check to see if an error occurred. We know if an error occurred because all
2856 // error codes are greater than 0.
2857 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
2858 sendErrorResponse(response,
2859 result.getInt(AccountManager.KEY_ERROR_CODE),
2860 result.getString(AccountManager.KEY_ERROR_MESSAGE));
2861 return;
2862 }
2863 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
2864 sendErrorResponse(
2865 response,
2866 AccountManager.ERROR_CODE_INVALID_RESPONSE,
2867 "no result in response");
2868 return;
2869 }
2870 final Bundle newResult = new Bundle();
2871 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
2872 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
2873 sendResponse(response, newResult);
2874 }
2875 }.bind();
2876 } finally {
2877 restoreCallingIdentity(identityToken);
2878 }
2879 }
2880
2881 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002882 public void editProperties(IAccountManagerResponse response, final String accountType,
2883 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002884 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002885 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2886 Log.v(TAG, "editProperties: accountType " + accountType
2887 + ", response " + response
2888 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002889 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002890 + ", pid " + Binder.getCallingPid());
2891 }
Fred Quintana382601f2010-03-25 12:25:10 -07002892 if (response == null) throw new IllegalArgumentException("response is null");
2893 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002894 int userId = UserHandle.getCallingUserId();
2895 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002896 String msg = String.format(
2897 "uid %s cannot edit authenticator properites for account type: %s",
2898 callingUid,
2899 accountType);
2900 throw new SecurityException(msg);
2901 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002902 long identityToken = clearCallingIdentity();
2903 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002904 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002905 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002906 true /* stripAuthTokenFromResult */, null /* accountName */,
2907 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002908 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002909 public void run() throws RemoteException {
2910 mAuthenticator.editProperties(this, mAccountType);
2911 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002912 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002913 protected String toDebugString(long now) {
2914 return super.toDebugString(now) + ", editProperties"
2915 + ", accountType " + accountType;
2916 }
2917 }.bind();
2918 } finally {
2919 restoreCallingIdentity(identityToken);
2920 }
Fred Quintana60307342009-03-24 22:48:12 -07002921 }
2922
Amith Yamasani12747872015-12-07 14:19:49 -08002923 @Override
2924 public boolean someUserHasAccount(@NonNull final Account account) {
2925 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
2926 throw new SecurityException("Only system can check for accounts across users");
2927 }
2928 final long token = Binder.clearCallingIdentity();
2929 try {
2930 AccountAndUser[] allAccounts = getAllAccounts();
2931 for (int i = allAccounts.length - 1; i >= 0; i--) {
2932 if (allAccounts[i].account.equals(account)) {
2933 return true;
2934 }
2935 }
2936 return false;
2937 } finally {
2938 Binder.restoreCallingIdentity(token);
2939 }
2940 }
2941
Fred Quintana33269202009-04-20 16:05:10 -07002942 private class GetAccountsByTypeAndFeatureSession extends Session {
2943 private final String[] mFeatures;
2944 private volatile Account[] mAccountsOfType = null;
2945 private volatile ArrayList<Account> mAccountsWithFeatures = null;
2946 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002947 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07002948
Amith Yamasani04e0d262012-02-14 11:50:53 -08002949 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002950 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002951 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002952 true /* stripAuthTokenFromResult */, null /* accountName */,
2953 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08002954 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07002955 mFeatures = features;
2956 }
2957
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002958 @Override
Fred Quintana33269202009-04-20 16:05:10 -07002959 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002960 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07002961 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
2962 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002963 }
Fred Quintana33269202009-04-20 16:05:10 -07002964 // check whether each account matches the requested features
2965 mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
2966 mCurrentAccount = 0;
2967
2968 checkAccount();
2969 }
2970
2971 public void checkAccount() {
2972 if (mCurrentAccount >= mAccountsOfType.length) {
2973 sendResult();
2974 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002975 }
Fred Quintana33269202009-04-20 16:05:10 -07002976
Fred Quintana29e94b82010-03-10 12:11:51 -08002977 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
2978 if (accountAuthenticator == null) {
2979 // It is possible that the authenticator has died, which is indicated by
2980 // mAuthenticator being set to null. If this happens then just abort.
2981 // There is no need to send back a result or error in this case since
2982 // that already happened when mAuthenticator was cleared.
2983 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2984 Log.v(TAG, "checkAccount: aborting session since we are no longer"
2985 + " connected to the authenticator, " + toDebugString());
2986 }
2987 return;
2988 }
Fred Quintana33269202009-04-20 16:05:10 -07002989 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08002990 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07002991 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002992 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07002993 }
2994 }
2995
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002996 @Override
Fred Quintana33269202009-04-20 16:05:10 -07002997 public void onResult(Bundle result) {
2998 mNumResults++;
2999 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003000 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07003001 return;
3002 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003003 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07003004 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
3005 }
3006 mCurrentAccount++;
3007 checkAccount();
3008 }
3009
3010 public void sendResult() {
3011 IAccountManagerResponse response = getResponseAndClose();
3012 if (response != null) {
3013 try {
3014 Account[] accounts = new Account[mAccountsWithFeatures.size()];
3015 for (int i = 0; i < accounts.length; i++) {
3016 accounts[i] = mAccountsWithFeatures.get(i);
3017 }
Fred Quintana56285a62010-12-02 14:20:51 -08003018 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3019 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3020 + response);
3021 }
Fred Quintana33269202009-04-20 16:05:10 -07003022 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003023 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07003024 response.onResult(result);
3025 } catch (RemoteException e) {
3026 // if the caller is dead then there is no one to care about remote exceptions
3027 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3028 Log.v(TAG, "failure while notifying response", e);
3029 }
3030 }
3031 }
3032 }
3033
3034
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003035 @Override
Fred Quintana33269202009-04-20 16:05:10 -07003036 protected String toDebugString(long now) {
3037 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
3038 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
3039 }
3040 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003041
Amith Yamasani04e0d262012-02-14 11:50:53 -08003042 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003043 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08003044 * @hide
3045 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003046 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003047 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003048 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07003049 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3050 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003051 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003052 return new Account[0];
3053 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003054 long identityToken = clearCallingIdentity();
3055 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003056 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003057 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003058 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003059 callingUid,
3060 null, // packageName
3061 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003062 } finally {
3063 restoreCallingIdentity(identityToken);
3064 }
3065 }
3066
Amith Yamasanif29f2362012-04-05 18:29:52 -07003067 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003068 * Returns accounts for all running users.
3069 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07003070 * @hide
3071 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003072 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003073 public AccountAndUser[] getRunningAccounts() {
3074 final int[] runningUserIds;
3075 try {
3076 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
3077 } catch (RemoteException e) {
3078 // Running in system_server; should never happen
3079 throw new RuntimeException(e);
3080 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003081 return getAccounts(runningUserIds);
3082 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07003083
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003084 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003085 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003086 public AccountAndUser[] getAllAccounts() {
3087 final List<UserInfo> users = getUserManager().getUsers();
3088 final int[] userIds = new int[users.size()];
3089 for (int i = 0; i < userIds.length; i++) {
3090 userIds[i] = users.get(i).id;
3091 }
3092 return getAccounts(userIds);
3093 }
3094
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003095 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07003096 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003097 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07003098 for (int userId : userIds) {
3099 UserAccounts userAccounts = getUserAccounts(userId);
3100 if (userAccounts == null) continue;
3101 synchronized (userAccounts.cacheLock) {
3102 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
3103 Binder.getCallingUid(), null);
3104 for (int a = 0; a < accounts.length; a++) {
3105 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07003106 }
3107 }
3108 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003109
3110 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
3111 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07003112 }
3113
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003114 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003115 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003116 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
3117 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07003118 }
3119
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003120 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003121 private Account[] getAccountsAsUser(
3122 String type,
3123 int userId,
3124 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07003125 int packageUid,
3126 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003127 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003128 // Only allow the system process to read accounts of other users
3129 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07003130 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08003131 && mContext.checkCallingOrSelfPermission(
3132 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
3133 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003134 throw new SecurityException("User " + UserHandle.getCallingUserId()
3135 + " trying to get account for " + userId);
3136 }
3137
Fred Quintana56285a62010-12-02 14:20:51 -08003138 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3139 Log.v(TAG, "getAccounts: accountType " + type
3140 + ", caller's uid " + Binder.getCallingUid()
3141 + ", pid " + Binder.getCallingPid());
3142 }
Amith Yamasani27db4682013-03-30 17:07:47 -07003143 // If the original calling app was using the framework account chooser activity, we'll
3144 // be passed in the original caller's uid here, which is what should be used for filtering.
3145 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
3146 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07003147 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07003148 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003149
Svetoslavf3f02ac2015-09-08 14:36:35 -07003150 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3151 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003152 if (visibleAccountTypes.isEmpty()
3153 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003154 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003155 } else if (visibleAccountTypes.contains(type)) {
3156 // Prune the list down to just the requested type.
3157 visibleAccountTypes = new ArrayList<>();
3158 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07003159 } // else aggregate all the visible accounts (it won't matter if the
3160 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003161
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003162 long identityToken = clearCallingIdentity();
3163 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003164 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003165 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003166 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003167 callingUid,
3168 callingPackage,
3169 visibleAccountTypes);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003170 } finally {
3171 restoreCallingIdentity(identityToken);
3172 }
3173 }
3174
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003175 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003176 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003177 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003178 int callingUid,
3179 String callingPackage,
3180 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07003181 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003182 ArrayList<Account> visibleAccounts = new ArrayList<>();
3183 for (String visibleType : visibleAccountTypes) {
3184 Account[] accountsForType = getAccountsFromCacheLocked(
3185 userAccounts, visibleType, callingUid, callingPackage);
3186 if (accountsForType != null) {
3187 visibleAccounts.addAll(Arrays.asList(accountsForType));
3188 }
3189 }
3190 Account[] result = new Account[visibleAccounts.size()];
3191 for (int i = 0; i < visibleAccounts.size(); i++) {
3192 result[i] = visibleAccounts.get(i);
3193 }
3194 return result;
3195 }
3196 }
3197
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003198 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003199 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
3200 checkManageUsersPermission("addSharedAccountsFromParentUser");
3201 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
3202 for (Account account : accounts) {
3203 addSharedAccountAsUser(account, userId);
3204 }
3205 }
3206
3207 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08003208 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003209 UserAccounts accounts = getUserAccounts(userId);
3210 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Amith Yamasani67df64b2012-12-14 12:09:36 -08003211 ContentValues values = new ContentValues();
3212 values.put(ACCOUNTS_NAME, account.name);
3213 values.put(ACCOUNTS_TYPE, account.type);
3214 db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3215 new String[] {account.name, account.type});
3216 long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
3217 if (accountId < 0) {
3218 Log.w(TAG, "insertAccountIntoDatabase: " + account
3219 + ", skipping the DB insert failed");
3220 return false;
3221 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003222 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08003223 return true;
3224 }
3225
3226 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003227 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
3228 userId = handleIncomingUser(userId);
3229 UserAccounts accounts = getUserAccounts(userId);
3230 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003231 long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003232 final ContentValues values = new ContentValues();
3233 values.put(ACCOUNTS_NAME, newName);
3234 values.put(ACCOUNTS_PREVIOUS_NAME, account.name);
3235 int r = db.update(
3236 TABLE_SHARED_ACCOUNTS,
3237 values,
3238 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3239 new String[] { account.name, account.type });
3240 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003241 int callingUid = getCallingUid();
3242 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
3243 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003244 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003245 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003246 }
3247 return r > 0;
3248 }
3249
3250 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08003251 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003252 return removeSharedAccountAsUser(account, userId, getCallingUid());
3253 }
3254
3255 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08003256 userId = handleIncomingUser(userId);
3257 UserAccounts accounts = getUserAccounts(userId);
3258 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003259 long sharedTableAccountId = getAccountIdFromSharedTable(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08003260 int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
3261 new String[] {account.name, account.type});
3262 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003263 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
3264 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07003265 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08003266 }
3267 return r > 0;
3268 }
3269
3270 @Override
3271 public Account[] getSharedAccountsAsUser(int userId) {
3272 userId = handleIncomingUser(userId);
3273 UserAccounts accounts = getUserAccounts(userId);
3274 ArrayList<Account> accountList = new ArrayList<Account>();
3275 Cursor cursor = null;
3276 try {
3277 cursor = accounts.openHelper.getReadableDatabase()
3278 .query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
3279 null, null, null, null, null);
3280 if (cursor != null && cursor.moveToFirst()) {
3281 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
3282 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
3283 do {
3284 accountList.add(new Account(cursor.getString(nameIndex),
3285 cursor.getString(typeIndex)));
3286 } while (cursor.moveToNext());
3287 }
3288 } finally {
3289 if (cursor != null) {
3290 cursor.close();
3291 }
3292 }
3293 Account[] accountArray = new Account[accountList.size()];
3294 accountList.toArray(accountArray);
3295 return accountArray;
3296 }
3297
3298 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003299 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003300 public Account[] getAccounts(String type, String opPackageName) {
3301 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003302 }
3303
Amith Yamasani27db4682013-03-30 17:07:47 -07003304 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003305 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003306 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003307 int callingUid = Binder.getCallingUid();
3308 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
3309 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
3310 + callingUid + " with uid=" + uid);
3311 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07003312 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
3313 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07003314 }
3315
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003316 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07003317 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07003318 public Account[] getAccountsByTypeForPackage(String type, String packageName,
3319 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003320 int packageUid = -1;
3321 try {
3322 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07003323 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
3324 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003325 } catch (RemoteException re) {
3326 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
3327 return new Account[0];
3328 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07003329 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
3330 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07003331 }
3332
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003333 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003334 public void getAccountsByFeatures(
3335 IAccountManagerResponse response,
3336 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07003337 String[] features,
3338 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003339 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003340 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3341 Log.v(TAG, "getAccounts: accountType " + type
3342 + ", response " + response
3343 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003344 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003345 + ", pid " + Binder.getCallingPid());
3346 }
Fred Quintana382601f2010-03-25 12:25:10 -07003347 if (response == null) throw new IllegalArgumentException("response is null");
3348 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003349 int userId = UserHandle.getCallingUserId();
3350
Svetoslavf3f02ac2015-09-08 14:36:35 -07003351 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
3352 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003353 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003354 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003355 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003356 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
3357 try {
3358 response.onResult(result);
3359 } catch (RemoteException e) {
3360 Log.e(TAG, "Cannot respond to caller do to exception." , e);
3361 }
3362 return;
3363 }
Fred Quintana33269202009-04-20 16:05:10 -07003364 long identityToken = clearCallingIdentity();
3365 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003366 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003367 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003368 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003369 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07003370 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003371 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08003372 Bundle result = new Bundle();
3373 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
3374 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003375 return;
3376 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003377 new GetAccountsByTypeAndFeatureSession(
3378 userAccounts,
3379 response,
3380 type,
3381 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003382 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07003383 } finally {
3384 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07003385 }
3386 }
3387
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003388 private long getAccountIdFromSharedTable(SQLiteDatabase db, Account account) {
3389 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
3390 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
3391 try {
3392 if (cursor.moveToNext()) {
3393 return cursor.getLong(0);
3394 }
3395 return -1;
3396 } finally {
3397 cursor.close();
3398 }
3399 }
3400
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003401 private long getAccountIdLocked(SQLiteDatabase db, Account account) {
Fred Quintana60307342009-03-24 22:48:12 -07003402 Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
Fred Quintanaffd0cb042009-08-15 21:45:26 -07003403 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
Fred Quintana60307342009-03-24 22:48:12 -07003404 try {
3405 if (cursor.moveToNext()) {
3406 return cursor.getLong(0);
3407 }
3408 return -1;
3409 } finally {
3410 cursor.close();
3411 }
3412 }
3413
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003414 private long getExtrasIdLocked(SQLiteDatabase db, long accountId, String key) {
Fred Quintana60307342009-03-24 22:48:12 -07003415 Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
3416 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
3417 new String[]{key}, null, null, null);
3418 try {
3419 if (cursor.moveToNext()) {
3420 return cursor.getLong(0);
3421 }
3422 return -1;
3423 } finally {
3424 cursor.close();
3425 }
3426 }
3427
Fred Quintanaa698f422009-04-08 19:14:54 -07003428 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07003429 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07003430 IAccountManagerResponse mResponse;
3431 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07003432 final boolean mExpectActivityLaunch;
3433 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003434 final String mAccountName;
3435 // Indicates if we need to add auth details(like last credential time)
3436 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003437 // If set, we need to update the last authenticated time. This is
3438 // currently
3439 // used on
3440 // successful confirming credentials.
3441 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07003442
Fred Quintana33269202009-04-20 16:05:10 -07003443 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07003444 private int mNumRequestContinued = 0;
3445 private int mNumErrors = 0;
3446
Fred Quintana60307342009-03-24 22:48:12 -07003447 IAccountAuthenticator mAuthenticator = null;
3448
Fred Quintana8570f742010-02-18 10:32:54 -08003449 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003450 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08003451
Amith Yamasani04e0d262012-02-14 11:50:53 -08003452 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003453 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3454 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003455 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
3456 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
3457 }
3458
3459 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
3460 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
3461 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07003462 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08003463 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07003464 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08003465 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08003466 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07003467 mResponse = response;
3468 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07003469 mExpectActivityLaunch = expectActivityLaunch;
3470 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003471 mAccountName = accountName;
3472 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003473 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003474
Fred Quintanaa698f422009-04-08 19:14:54 -07003475 synchronized (mSessions) {
3476 mSessions.put(toString(), this);
3477 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08003478 if (response != null) {
3479 try {
3480 response.asBinder().linkToDeath(this, 0 /* flags */);
3481 } catch (RemoteException e) {
3482 mResponse = null;
3483 binderDied();
3484 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003485 }
Fred Quintana60307342009-03-24 22:48:12 -07003486 }
3487
Fred Quintanaa698f422009-04-08 19:14:54 -07003488 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07003489 if (mResponse == null) {
3490 // this session has already been closed
3491 return null;
3492 }
Fred Quintana60307342009-03-24 22:48:12 -07003493 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07003494 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07003495 return response;
3496 }
3497
Fred Quintanaa698f422009-04-08 19:14:54 -07003498 private void close() {
3499 synchronized (mSessions) {
3500 if (mSessions.remove(toString()) == null) {
3501 // the session was already closed, so bail out now
3502 return;
3503 }
3504 }
3505 if (mResponse != null) {
3506 // stop listening for response deaths
3507 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
3508
3509 // clear this so that we don't accidentally send any further results
3510 mResponse = null;
3511 }
3512 cancelTimeout();
3513 unbind();
3514 }
3515
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003516 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003517 public void binderDied() {
3518 mResponse = null;
3519 close();
3520 }
3521
3522 protected String toDebugString() {
3523 return toDebugString(SystemClock.elapsedRealtime());
3524 }
3525
3526 protected String toDebugString(long now) {
3527 return "Session: expectLaunch " + mExpectActivityLaunch
3528 + ", connected " + (mAuthenticator != null)
3529 + ", stats (" + mNumResults + "/" + mNumRequestContinued
3530 + "/" + mNumErrors + ")"
3531 + ", lifetime " + ((now - mCreationTime) / 1000.0);
3532 }
3533
Fred Quintana60307342009-03-24 22:48:12 -07003534 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07003535 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3536 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
3537 }
Fred Quintanab839afc2009-10-14 15:57:28 -07003538 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003539 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003540 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07003541 }
3542 }
3543
3544 private void unbind() {
3545 if (mAuthenticator != null) {
3546 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07003547 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07003548 }
3549 }
3550
Fred Quintana60307342009-03-24 22:48:12 -07003551 public void cancelTimeout() {
3552 mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
3553 }
3554
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003555 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07003556 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07003557 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07003558 try {
3559 run();
3560 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003561 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07003562 "remote exception");
3563 }
Fred Quintana60307342009-03-24 22:48:12 -07003564 }
3565
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003566 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07003567 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003568 mAuthenticator = null;
3569 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07003570 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07003571 try {
3572 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3573 "disconnected");
3574 } catch (RemoteException e) {
3575 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3576 Log.v(TAG, "Session.onServiceDisconnected: "
3577 + "caught RemoteException while responding", e);
3578 }
3579 }
Fred Quintana60307342009-03-24 22:48:12 -07003580 }
3581 }
3582
Fred Quintanab839afc2009-10-14 15:57:28 -07003583 public abstract void run() throws RemoteException;
3584
Fred Quintana60307342009-03-24 22:48:12 -07003585 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07003586 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07003587 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07003588 try {
3589 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
3590 "timeout");
3591 } catch (RemoteException e) {
3592 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3593 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
3594 e);
3595 }
3596 }
Fred Quintana60307342009-03-24 22:48:12 -07003597 }
3598 }
3599
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003600 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003601 public void onResult(Bundle result) {
3602 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003603 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003604 if (result != null) {
3605 boolean isSuccessfulConfirmCreds = result.getBoolean(
3606 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003607 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003608 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
3609 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07003610 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003611 // mUpdateLastAuthenticatedTime is true and the confirmRequest
3612 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07003613 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003614 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003615 if (needUpdate || mAuthDetailsRequired) {
3616 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
3617 if (needUpdate && accountPresent) {
3618 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
3619 }
3620 if (mAuthDetailsRequired) {
3621 long lastAuthenticatedTime = -1;
3622 if (accountPresent) {
3623 lastAuthenticatedTime = DatabaseUtils.longForQuery(
3624 mAccounts.openHelper.getReadableDatabase(),
3625 "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
3626 + " from " +
3627 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
3628 + ACCOUNTS_TYPE + "=?",
3629 new String[] {
3630 mAccountName, mAccountType
3631 });
3632 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07003633 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003634 lastAuthenticatedTime);
3635 }
3636 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003637 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003638 if (result != null
3639 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
3640 /*
3641 * The Authenticator API allows third party authenticators to
3642 * supply arbitrary intents to other apps that they can run,
3643 * this can be very bad when those apps are in the system like
3644 * the System Settings.
3645 */
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003646 int authenticatorUid = Binder.getCallingUid();
Carlos Valdivia416747a2013-10-14 17:22:42 -07003647 long bid = Binder.clearCallingIdentity();
3648 try {
3649 PackageManager pm = mContext.getPackageManager();
3650 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
3651 int targetUid = resolveInfo.activityInfo.applicationInfo.uid;
3652 if (PackageManager.SIGNATURE_MATCH !=
3653 pm.checkSignatures(authenticatorUid, targetUid)) {
3654 throw new SecurityException(
3655 "Activity to be started with KEY_INTENT must " +
3656 "share Authenticator's signatures");
3657 }
3658 } finally {
3659 Binder.restoreCallingIdentity(bid);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003660 }
3661 }
3662 if (result != null
3663 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003664 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
3665 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003666 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
3667 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003668 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
3669 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003670 }
Fred Quintana60307342009-03-24 22:48:12 -07003671 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003672 IAccountManagerResponse response;
3673 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003674 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003675 response = mResponse;
3676 } else {
3677 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07003678 }
Fred Quintana60307342009-03-24 22:48:12 -07003679 if (response != null) {
3680 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07003681 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08003682 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3683 Log.v(TAG, getClass().getSimpleName()
3684 + " calling onError() on response " + response);
3685 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003686 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07003687 "null bundle returned");
3688 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08003689 if (mStripAuthTokenFromResult) {
3690 result.remove(AccountManager.KEY_AUTHTOKEN);
3691 }
Fred Quintana56285a62010-12-02 14:20:51 -08003692 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3693 Log.v(TAG, getClass().getSimpleName()
3694 + " calling onResult() on response " + response);
3695 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003696 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
3697 (intent == null)) {
3698 // All AccountManager error codes are greater than 0
3699 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
3700 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3701 } else {
3702 response.onResult(result);
3703 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003704 }
Fred Quintana60307342009-03-24 22:48:12 -07003705 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003706 // if the caller is dead then there is no one to care about remote exceptions
3707 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3708 Log.v(TAG, "failure while notifying response", e);
3709 }
Fred Quintana60307342009-03-24 22:48:12 -07003710 }
3711 }
3712 }
Fred Quintana60307342009-03-24 22:48:12 -07003713
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003714 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003715 public void onRequestContinued() {
3716 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07003717 }
3718
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003719 @Override
Fred Quintana60307342009-03-24 22:48:12 -07003720 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07003721 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07003722 IAccountManagerResponse response = getResponseAndClose();
3723 if (response != null) {
3724 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08003725 Log.v(TAG, getClass().getSimpleName()
3726 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07003727 }
3728 try {
3729 response.onError(errorCode, errorMessage);
3730 } catch (RemoteException e) {
3731 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3732 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
3733 }
3734 }
3735 } else {
3736 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3737 Log.v(TAG, "Session.onError: already closed");
3738 }
Fred Quintana60307342009-03-24 22:48:12 -07003739 }
3740 }
Fred Quintanab839afc2009-10-14 15:57:28 -07003741
3742 /**
3743 * find the component name for the authenticator and initiate a bind
3744 * if no authenticator or the bind fails then return false, otherwise return true
3745 */
3746 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07003747 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
3748 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
3749 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07003750 if (authenticatorInfo == null) {
3751 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3752 Log.v(TAG, "there is no authenticator for " + authenticatorType
3753 + ", bailing out");
3754 }
3755 return false;
3756 }
3757
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07003758 final ActivityManager am = mContext.getSystemService(ActivityManager.class);
3759 if (am.isUserRunningAndLocked(mAccounts.userId)
3760 && !authenticatorInfo.componentInfo.encryptionAware) {
3761 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
3762 + " which isn't encryption aware");
3763 return false;
3764 }
3765
Fred Quintanab839afc2009-10-14 15:57:28 -07003766 Intent intent = new Intent();
3767 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
3768 intent.setComponent(authenticatorInfo.componentName);
3769 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3770 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
3771 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08003772 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
3773 new UserHandle(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07003774 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3775 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
3776 }
3777 return false;
3778 }
3779
Fred Quintanab839afc2009-10-14 15:57:28 -07003780 return true;
3781 }
Fred Quintana60307342009-03-24 22:48:12 -07003782 }
3783
3784 private class MessageHandler extends Handler {
3785 MessageHandler(Looper looper) {
3786 super(looper);
3787 }
Costin Manolache3348f142009-09-29 18:58:36 -07003788
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003789 @Override
Fred Quintana60307342009-03-24 22:48:12 -07003790 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07003791 switch (msg.what) {
3792 case MESSAGE_TIMED_OUT:
3793 Session session = (Session)msg.obj;
3794 session.onTimedOut();
3795 break;
3796
Amith Yamasani5be347b2013-03-31 17:44:31 -07003797 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00003798 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07003799 break;
3800
Fred Quintana60307342009-03-24 22:48:12 -07003801 default:
3802 throw new IllegalStateException("unhandled message: " + msg.what);
3803 }
3804 }
3805 }
3806
Amith Yamasani04e0d262012-02-14 11:50:53 -08003807 private static String getDatabaseName(int userId) {
Jeff Sharkey8212ae02016-02-10 14:46:43 -07003808 File systemDir = Environment.getDataSystemDirectory();
Amith Yamasani61f57372012-08-31 12:12:28 -07003809 File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003810 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07003811 // Migrate old file, if it exists, to the new location.
3812 // Make sure the new file doesn't already exist. A dummy file could have been
3813 // accidentally created in the old location, causing the new one to become corrupted
3814 // as well.
Amith Yamasani04e0d262012-02-14 11:50:53 -08003815 File oldFile = new File(systemDir, DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07003816 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07003817 // Check for use directory; create if it doesn't exist, else renameTo will fail
Amith Yamasani61f57372012-08-31 12:12:28 -07003818 File userDir = Environment.getUserSystemDirectory(userId);
Marc Blankc6b0f992012-03-18 19:16:41 -07003819 if (!userDir.exists()) {
3820 if (!userDir.mkdirs()) {
3821 throw new IllegalStateException("User dir cannot be created: " + userDir);
3822 }
3823 }
3824 if (!oldFile.renameTo(databaseFile)) {
3825 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
3826 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003827 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08003828 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08003829 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08003830 }
3831
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003832 private static class DebugDbHelper{
3833 private DebugDbHelper() {
3834 }
3835
3836 private static String TABLE_DEBUG = "debug_table";
3837
3838 // Columns for the table
3839 private static String ACTION_TYPE = "action_type";
3840 private static String TIMESTAMP = "time";
3841 private static String CALLER_UID = "caller_uid";
3842 private static String TABLE_NAME = "table_name";
3843 private static String KEY = "primary_key";
3844
3845 // These actions correspond to the occurrence of real actions. Since
3846 // these are called by the authenticators, the uid associated will be
3847 // of the authenticator.
3848 private static String ACTION_SET_PASSWORD = "action_set_password";
3849 private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
3850 private static String ACTION_ACCOUNT_ADD = "action_account_add";
3851 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
3852 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
3853 private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
3854
3855 // These actions don't necessarily correspond to any action on
3856 // accountDb taking place. As an example, there might be a request for
3857 // addingAccount, which might not lead to addition of account on grounds
3858 // of bad authentication. We will still be logging it to keep track of
3859 // who called.
3860 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
3861 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003862
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003863 //This action doesn't add account to accountdb. Account is only
3864 // added in finishSession which may be in a different user profile.
Sandra Kwan78812282015-11-04 11:19:47 -08003865 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003866 private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
3867 "action_called_account_session_finish";
Sandra Kwan78812282015-11-04 11:19:47 -08003868
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003869 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
3870
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003871 private static void createDebugTable(SQLiteDatabase db) {
3872 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
3873 + ACCOUNTS_ID + " INTEGER,"
3874 + ACTION_TYPE + " TEXT NOT NULL, "
3875 + TIMESTAMP + " DATETIME,"
3876 + CALLER_UID + " INTEGER NOT NULL,"
3877 + TABLE_NAME + " TEXT NOT NULL,"
3878 + KEY + " INTEGER PRIMARY KEY)");
3879 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
3880 }
3881 }
3882
3883 private void logRecord(UserAccounts accounts, String action, String tableName) {
3884 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3885 logRecord(db, action, tableName, -1, accounts);
3886 }
3887
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003888 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
3889 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
3890 logRecord(db, action, tableName, -1, accounts, uid);
3891 }
3892
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07003893 /*
3894 * This function receives an opened writable database.
3895 */
3896 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3897 UserAccounts userAccount) {
3898 logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
3899 }
3900
3901 /*
3902 * This function receives an opened writable database.
3903 */
3904 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
3905 UserAccounts userAccount, int callingUid) {
3906 SQLiteStatement logStatement = userAccount.statementForLogging;
3907 logStatement.bindLong(1, accountId);
3908 logStatement.bindString(2, action);
3909 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
3910 logStatement.bindLong(4, callingUid);
3911 logStatement.bindString(5, tableName);
3912 logStatement.bindLong(6, userAccount.debugDbInsertionPoint);
3913 logStatement.execute();
3914 logStatement.clearBindings();
3915 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
3916 % MAX_DEBUG_DB_SIZE;
3917 }
3918
3919 /*
3920 * This should only be called once to compile the sql statement for logging
3921 * and to find the insertion point.
3922 */
3923 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
3924 UserAccounts userAccount) {
3925 // Initialize the count if not done earlier.
3926 int size = (int) getDebugTableRowCount(db);
3927 if (size >= MAX_DEBUG_DB_SIZE) {
3928 // Table is full, and we need to find the point where to insert.
3929 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
3930 } else {
3931 userAccount.debugDbInsertionPoint = size;
3932 }
3933 compileSqlStatementForLogging(db, userAccount);
3934 }
3935
3936 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
3937 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
3938 + " VALUES (?,?,?,?,?,?)";
3939 userAccount.statementForLogging = db.compileStatement(sql);
3940 }
3941
3942 private long getDebugTableRowCount(SQLiteDatabase db) {
3943 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
3944 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
3945 }
3946
3947 /*
3948 * Finds the row key where the next insertion should take place. This should
3949 * be invoked only if the table has reached its full capacity.
3950 */
3951 private long getDebugTableInsertionPoint(SQLiteDatabase db) {
3952 // This query finds the smallest timestamp value (and if 2 records have
3953 // same timestamp, the choose the lower id).
3954 String queryCountDebugDbRows = new StringBuilder()
3955 .append("SELECT ").append(DebugDbHelper.KEY)
3956 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
3957 .append(" ORDER BY ")
3958 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
3959 .append(" LIMIT 1")
3960 .toString();
3961 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
3962 }
3963
Amith Yamasani04e0d262012-02-14 11:50:53 -08003964 static class DatabaseHelper extends SQLiteOpenHelper {
Oscar Montemayora8529f62009-11-18 10:14:20 -08003965
Amith Yamasani04e0d262012-02-14 11:50:53 -08003966 public DatabaseHelper(Context context, int userId) {
3967 super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
Fred Quintana60307342009-03-24 22:48:12 -07003968 }
3969
Fred Quintanaf9f240e2011-02-24 18:27:50 -08003970 /**
3971 * This call needs to be made while the mCacheLock is held. The way to
3972 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
3973 * @param db The database.
3974 */
Fred Quintana60307342009-03-24 22:48:12 -07003975 @Override
3976 public void onCreate(SQLiteDatabase db) {
3977 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
3978 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3979 + ACCOUNTS_NAME + " TEXT NOT NULL, "
3980 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
3981 + ACCOUNTS_PASSWORD + " TEXT, "
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07003982 + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003983 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
Fred Quintana60307342009-03-24 22:48:12 -07003984 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
3985
3986 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
3987 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3988 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
3989 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
3990 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
3991 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
3992
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003993 createGrantsTable(db);
3994
Fred Quintana60307342009-03-24 22:48:12 -07003995 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
3996 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
3997 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
3998 + EXTRAS_KEY + " TEXT NOT NULL, "
3999 + EXTRAS_VALUE + " TEXT, "
4000 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
4001
4002 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
4003 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
4004 + META_VALUE + " TEXT)");
Fred Quintanaa698f422009-04-08 19:14:54 -07004005
Amith Yamasani67df64b2012-12-14 12:09:36 -08004006 createSharedAccountsTable(db);
4007
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004008 createAccountsDeletionTrigger(db);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004009
4010 DebugDbHelper.createDebugTable(db);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004011 }
4012
Amith Yamasani67df64b2012-12-14 12:09:36 -08004013 private void createSharedAccountsTable(SQLiteDatabase db) {
4014 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
4015 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
4016 + ACCOUNTS_NAME + " TEXT NOT NULL, "
4017 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
4018 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
4019 }
4020
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004021 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
4022 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
4023 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
4024 }
4025
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004026 private void addOldAccountNameColumn(SQLiteDatabase db) {
4027 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
4028 }
4029
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004030 private void addDebugTable(SQLiteDatabase db) {
4031 DebugDbHelper.createDebugTable(db);
4032 }
4033
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004034 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004035 db.execSQL(""
4036 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
4037 + " BEGIN"
4038 + " DELETE FROM " + TABLE_AUTHTOKENS
4039 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
4040 + " DELETE FROM " + TABLE_EXTRAS
4041 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004042 + " DELETE FROM " + TABLE_GRANTS
4043 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07004044 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07004045 }
4046
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004047 private void createGrantsTable(SQLiteDatabase db) {
4048 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
4049 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
4050 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
4051 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
4052 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
4053 + "," + GRANTS_GRANTEE_UID + "))");
4054 }
4055
Fred Quintana60307342009-03-24 22:48:12 -07004056 @Override
4057 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004058 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07004059
Fred Quintanaa698f422009-04-08 19:14:54 -07004060 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004061 // no longer need to do anything since the work is done
4062 // when upgrading from version 2
4063 oldVersion++;
4064 }
4065
4066 if (oldVersion == 2) {
4067 createGrantsTable(db);
4068 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
4069 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07004070 oldVersion++;
4071 }
Costin Manolache3348f142009-09-29 18:58:36 -07004072
4073 if (oldVersion == 3) {
4074 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
4075 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
4076 oldVersion++;
4077 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004078
4079 if (oldVersion == 4) {
4080 createSharedAccountsTable(db);
4081 oldVersion++;
4082 }
4083
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004084 if (oldVersion == 5) {
4085 addOldAccountNameColumn(db);
4086 oldVersion++;
4087 }
4088
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004089 if (oldVersion == 6) {
4090 addLastSuccessfullAuthenticatedTimeColumn(db);
4091 oldVersion++;
4092 }
4093
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004094 if (oldVersion == 7) {
4095 addDebugTable(db);
4096 oldVersion++;
4097 }
4098
Amith Yamasani67df64b2012-12-14 12:09:36 -08004099 if (oldVersion != newVersion) {
4100 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
4101 }
Fred Quintana60307342009-03-24 22:48:12 -07004102 }
4103
4104 @Override
4105 public void onOpen(SQLiteDatabase db) {
4106 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
4107 }
4108 }
4109
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004110 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07004111 return asBinder();
4112 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004113
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004114 /**
4115 * Searches array of arguments for the specified string
4116 * @param args array of argument strings
4117 * @param value value to search for
4118 * @return true if the value is contained in the array
4119 */
4120 private static boolean scanArgs(String[] args, String value) {
4121 if (args != null) {
4122 for (String arg : args) {
4123 if (value.equals(arg)) {
4124 return true;
4125 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004126 }
4127 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004128 return false;
4129 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004130
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004131 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004132 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07004133 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
4134 != PackageManager.PERMISSION_GRANTED) {
4135 fout.println("Permission Denial: can't dump AccountsManager from from pid="
4136 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
4137 + " without permission " + android.Manifest.permission.DUMP);
4138 return;
4139 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004140 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004141 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07004142
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004143 final List<UserInfo> users = getUserManager().getUsers();
4144 for (UserInfo user : users) {
4145 ipw.println("User " + user + ":");
4146 ipw.increaseIndent();
4147 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
4148 ipw.println();
4149 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08004150 }
4151 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004152
Amith Yamasani04e0d262012-02-14 11:50:53 -08004153 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
4154 String[] args, boolean isCheckinRequest) {
4155 synchronized (userAccounts.cacheLock) {
4156 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004157
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004158 if (isCheckinRequest) {
4159 // This is a checkin request. *Only* upload the account types and the count of each.
4160 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
4161 null, null, ACCOUNTS_TYPE, null, null);
4162 try {
4163 while (cursor.moveToNext()) {
4164 // print type,count
4165 fout.println(cursor.getString(0) + "," + cursor.getString(1));
4166 }
4167 } finally {
4168 if (cursor != null) {
4169 cursor.close();
4170 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004171 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004172 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004173 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07004174 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004175 fout.println("Accounts: " + accounts.length);
4176 for (Account account : accounts) {
4177 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004178 }
Fred Quintana307da1a2010-01-21 14:24:20 -08004179
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004180 // Add debug information.
4181 fout.println();
4182 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
4183 null, null, null, null, DebugDbHelper.TIMESTAMP);
4184 fout.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
4185 fout.println("Accounts History");
4186 try {
4187 while (cursor.moveToNext()) {
4188 // print type,count
4189 fout.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
4190 cursor.getString(2) + "," + cursor.getString(3) + ","
4191 + cursor.getString(4) + "," + cursor.getString(5));
4192 }
4193 } finally {
4194 cursor.close();
4195 }
4196
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004197 fout.println();
4198 synchronized (mSessions) {
4199 final long now = SystemClock.elapsedRealtime();
4200 fout.println("Active Sessions: " + mSessions.size());
4201 for (Session session : mSessions.values()) {
4202 fout.println(" " + session.toDebugString(now));
4203 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004204 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004205
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004206 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004207 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004208 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07004209 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004210 }
4211
Amith Yamasani04e0d262012-02-14 11:50:53 -08004212 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Dianne Hackborn41203752012-08-31 14:05:51 -07004213 Intent intent, int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004214 long identityToken = clearCallingIdentity();
4215 try {
4216 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4217 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
4218 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004219
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004220 if (intent.getComponent() != null &&
4221 GrantCredentialsPermissionActivity.class.getName().equals(
4222 intent.getComponent().getClassName())) {
Dianne Hackborn41203752012-08-31 14:05:51 -07004223 createNoCredentialsPermissionNotification(account, intent, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004224 } else {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004225 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07004226 intent.addCategory(String.valueOf(notificationId));
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004227 UserHandle user = new UserHandle(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004228 Context contextForUser = getContextForUser(user);
Fred Quintana33f889a2009-09-14 17:31:26 -07004229 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004230 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04004231 Notification n = new Notification.Builder(contextForUser)
4232 .setWhen(0)
4233 .setSmallIcon(android.R.drawable.stat_sys_warning)
4234 .setColor(contextForUser.getColor(
4235 com.android.internal.R.color.system_notification_accent_color))
4236 .setContentTitle(String.format(notificationTitleFormat, account.name))
4237 .setContentText(message)
4238 .setContentIntent(PendingIntent.getActivityAsUser(
4239 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
4240 null, user))
4241 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004242 installNotification(notificationId, n, user);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004243 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004244 } finally {
4245 restoreCallingIdentity(identityToken);
4246 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004247 }
4248
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004249 protected void installNotification(final int notificationId, final Notification n,
4250 UserHandle user) {
Fred Quintana56285a62010-12-02 14:20:51 -08004251 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004252 .notifyAsUser(null, notificationId, n, user);
Fred Quintana56285a62010-12-02 14:20:51 -08004253 }
4254
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004255 protected void cancelNotification(int id, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004256 long identityToken = clearCallingIdentity();
4257 try {
4258 ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004259 .cancelAsUser(null, id, user);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07004260 } finally {
4261 restoreCallingIdentity(identityToken);
4262 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004263 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004264
Carlos Valdiviabf33bd42016-02-02 20:20:42 +00004265 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
4266 for (String perm : permissions) {
4267 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
4268 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4269 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
4270 }
4271 final int opCode = AppOpsManager.permissionToOpCode(perm);
4272 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
4273 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
4274 return true;
4275 }
4276 }
4277 }
4278 return false;
4279 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004280
Amith Yamasani67df64b2012-12-14 12:09:36 -08004281 private int handleIncomingUser(int userId) {
4282 try {
4283 return ActivityManagerNative.getDefault().handleIncomingUser(
4284 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
4285 } catch (RemoteException re) {
4286 // Shouldn't happen, local.
4287 }
4288 return userId;
4289 }
4290
Christopher Tateccbf84f2013-05-08 15:25:41 -07004291 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004292 final int callingUserId = UserHandle.getUserId(callingUid);
4293
4294 final PackageManager userPackageManager;
4295 try {
4296 userPackageManager = mContext.createPackageContextAsUser(
4297 "android", 0, new UserHandle(callingUserId)).getPackageManager();
4298 } catch (NameNotFoundException e) {
4299 return false;
4300 }
4301
4302 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07004303 for (String name : packages) {
4304 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004305 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08004306 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08004307 && (packageInfo.applicationInfo.privateFlags
4308 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07004309 return true;
4310 }
4311 } catch (PackageManager.NameNotFoundException e) {
4312 return false;
4313 }
4314 }
4315 return false;
4316 }
4317
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004318 private boolean permissionIsGranted(
4319 Account account, String authTokenType, int callerUid, int userId) {
Christopher Tateccbf84f2013-05-08 15:25:41 -07004320 final boolean isPrivileged = isPrivileged(callerUid);
Fred Quintana31957f12009-10-21 13:43:10 -07004321 final boolean fromAuthenticator = account != null
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004322 && isAccountManagedByCaller(account.type, callerUid, userId);
Fred Quintana31957f12009-10-21 13:43:10 -07004323 final boolean hasExplicitGrants = account != null
Amith Yamasani04e0d262012-02-14 11:50:53 -08004324 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004325 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4326 Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
Fred Quintana56285a62010-12-02 14:20:51 -08004327 + callerUid + ", " + account
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004328 + ": is authenticator? " + fromAuthenticator
4329 + ", has explicit permission? " + hasExplicitGrants);
4330 }
Christopher Tateccbf84f2013-05-08 15:25:41 -07004331 return fromAuthenticator || hasExplicitGrants || isPrivileged;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004332 }
4333
Svetoslavf3f02ac2015-09-08 14:36:35 -07004334 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
4335 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004336 if (accountType == null) {
4337 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004338 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07004339 return getTypesVisibleToCaller(callingUid, userId,
4340 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004341 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004342 }
4343
4344 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
4345 if (accountType == null) {
4346 return false;
4347 } else {
4348 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
4349 }
4350 }
4351
Svetoslavf3f02ac2015-09-08 14:36:35 -07004352 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
4353 String opPackageName) {
Carlos Valdiviabf33bd42016-02-02 20:20:42 +00004354 boolean isPermitted =
4355 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
4356 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004357 return getTypesForCaller(callingUid, userId, isPermitted);
4358 }
4359
4360 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
4361 return getTypesForCaller(callingUid, userId, false);
4362 }
4363
4364 private List<String> getTypesForCaller(
4365 int callingUid, int userId, boolean isOtherwisePermitted) {
4366 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004367 long identityToken = Binder.clearCallingIdentity();
4368 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
4369 try {
4370 serviceInfos = mAuthenticatorCache.getAllServices(userId);
4371 } finally {
4372 Binder.restoreCallingIdentity(identityToken);
4373 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004374 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004375 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004376 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
4377 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
4378 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004379 }
4380 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004381 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004382 }
4383
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004384 private boolean isAccountPresentForCaller(String accountName, String accountType) {
4385 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
4386 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
4387 if (account.name.equals(accountName)) {
4388 return true;
4389 }
4390 }
4391 }
4392 return false;
4393 }
4394
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004395 private static void checkManageUsersPermission(String message) {
4396 if (ActivityManager.checkComponentPermission(
4397 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
4398 != PackageManager.PERMISSION_GRANTED) {
4399 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
4400 }
4401 }
4402
Amith Yamasani04e0d262012-02-14 11:50:53 -08004403 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
4404 int callerUid) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004405 if (callerUid == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004406 return true;
4407 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004408 UserAccounts accounts = getUserAccountsForCaller();
4409 synchronized (accounts.cacheLock) {
4410 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4411 String[] args = { String.valueOf(callerUid), authTokenType,
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004412 account.name, account.type};
4413 final boolean permissionGranted =
4414 DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
4415 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
4416 // TODO: Skip this check when running automated tests. Replace this
4417 // with a more general solution.
4418 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08004419 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004420 + " but ignoring since device is in test harness.");
4421 return true;
4422 }
4423 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004424 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004425 }
4426
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07004427 private boolean isSystemUid(int callingUid) {
4428 String[] packages = null;
4429 long ident = Binder.clearCallingIdentity();
4430 try {
4431 packages = mPackageManager.getPackagesForUid(callingUid);
4432 } finally {
4433 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07004434 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07004435 if (packages != null) {
4436 for (String name : packages) {
4437 try {
4438 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
4439 if (packageInfo != null
4440 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
4441 != 0) {
4442 return true;
4443 }
4444 } catch (PackageManager.NameNotFoundException e) {
4445 Log.w(TAG, String.format("Could not find package [%s]", name), e);
4446 }
4447 }
4448 } else {
4449 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07004450 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07004451 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00004452 }
4453
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004454 /** Succeeds if any of the specified permissions are granted. */
4455 private void checkReadAccountsPermitted(
4456 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004457 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004458 int userId,
4459 String opPackageName) {
4460 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004461 String msg = String.format(
4462 "caller uid %s cannot access %s accounts",
4463 callingUid,
4464 accountType);
4465 Log.w(TAG, " " + msg);
4466 throw new SecurityException(msg);
4467 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004468 }
4469
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00004470 private boolean canUserModifyAccounts(int userId, int callingUid) {
4471 // the managing app can always modify accounts
4472 if (isProfileOwner(callingUid)) {
4473 return true;
4474 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01004475 if (getUserManager().getUserRestrictions(new UserHandle(userId))
4476 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
4477 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08004478 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01004479 return true;
4480 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01004481
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00004482 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
4483 // the managing app can always modify accounts
4484 if (isProfileOwner(callingUid)) {
4485 return true;
4486 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01004487 DevicePolicyManager dpm = (DevicePolicyManager) mContext
4488 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01004489 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02004490 if (typesArray == null) {
4491 return true;
4492 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01004493 for (String forbiddenType : typesArray) {
4494 if (forbiddenType.equals(accountType)) {
4495 return false;
4496 }
4497 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08004498 return true;
4499 }
4500
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00004501 private boolean isProfileOwner(int uid) {
4502 final DevicePolicyManagerInternal dpmi =
4503 LocalServices.getService(DevicePolicyManagerInternal.class);
4504 return (dpmi != null)
4505 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
4506 }
4507
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004508 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07004509 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
4510 throws RemoteException {
4511 final int callingUid = getCallingUid();
4512
Amith Yamasani27db4682013-03-30 17:07:47 -07004513 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07004514 throw new SecurityException();
4515 }
4516
4517 if (value) {
4518 grantAppPermission(account, authTokenType, uid);
4519 } else {
4520 revokeAppPermission(account, authTokenType, uid);
4521 }
4522 }
4523
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004524 /**
4525 * Allow callers with the given uid permission to get credentials for account/authTokenType.
4526 * <p>
4527 * Although this is public it can only be accessed via the AccountManagerService object
4528 * which is in the system. This means we don't need to protect it with permissions.
4529 * @hide
4530 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07004531 private void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07004532 if (account == null || authTokenType == null) {
4533 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07004534 return;
4535 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07004536 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08004537 synchronized (accounts.cacheLock) {
4538 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004539 db.beginTransaction();
4540 try {
4541 long accountId = getAccountIdLocked(db, account);
4542 if (accountId >= 0) {
4543 ContentValues values = new ContentValues();
4544 values.put(GRANTS_ACCOUNTS_ID, accountId);
4545 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
4546 values.put(GRANTS_GRANTEE_UID, uid);
4547 db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
4548 db.setTransactionSuccessful();
4549 }
4550 } finally {
4551 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004552 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004553 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
4554 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004555 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004556 }
4557
4558 /**
4559 * Don't allow callers with the given uid permission to get credentials for
4560 * account/authTokenType.
4561 * <p>
4562 * Although this is public it can only be accessed via the AccountManagerService object
4563 * which is in the system. This means we don't need to protect it with permissions.
4564 * @hide
4565 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07004566 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07004567 if (account == null || authTokenType == null) {
4568 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07004569 return;
4570 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07004571 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08004572 synchronized (accounts.cacheLock) {
4573 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004574 db.beginTransaction();
4575 try {
4576 long accountId = getAccountIdLocked(db, account);
4577 if (accountId >= 0) {
4578 db.delete(TABLE_GRANTS,
4579 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
4580 + GRANTS_GRANTEE_UID + "=?",
4581 new String[]{String.valueOf(accountId), authTokenType,
4582 String.valueOf(uid)});
4583 db.setTransactionSuccessful();
4584 }
4585 } finally {
4586 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004587 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004588 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
4589 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004590 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004591 }
Fred Quintana56285a62010-12-02 14:20:51 -08004592
4593 static final private String stringArrayToString(String[] value) {
4594 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
4595 }
4596
Amith Yamasani04e0d262012-02-14 11:50:53 -08004597 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
4598 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004599 if (oldAccountsForType != null) {
4600 ArrayList<Account> newAccountsList = new ArrayList<Account>();
4601 for (Account curAccount : oldAccountsForType) {
4602 if (!curAccount.equals(account)) {
4603 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08004604 }
4605 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004606 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004607 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004608 } else {
4609 Account[] newAccountsForType = new Account[newAccountsList.size()];
4610 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004611 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004612 }
Fred Quintana56285a62010-12-02 14:20:51 -08004613 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004614 accounts.userDataCache.remove(account);
4615 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004616 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08004617 }
4618
4619 /**
4620 * This assumes that the caller has already checked that the account is not already present.
4621 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08004622 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
4623 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004624 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
4625 Account[] newAccountsForType = new Account[oldLength + 1];
4626 if (accountsForType != null) {
4627 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08004628 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004629 newAccountsForType[oldLength] = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004630 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08004631 }
4632
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004633 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07004634 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004635 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07004636 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004637 return unfiltered;
4638 }
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004639 UserInfo user = mUserManager.getUserInfo(userAccounts.userId);
4640 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004641 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004642 // If any of the packages is a white listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004643 // otherwise return non-shared accounts only.
4644 // This might be a temporary way to specify a whitelist
4645 String whiteList = mContext.getResources().getString(
4646 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
4647 for (String packageName : packages) {
4648 if (whiteList.contains(";" + packageName + ";")) {
4649 return unfiltered;
4650 }
4651 }
4652 ArrayList<Account> allowed = new ArrayList<Account>();
4653 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
4654 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004655 String requiredAccountType = "";
4656 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07004657 // If there's an explicit callingPackage specified, check if that package
4658 // opted in to see restricted accounts.
4659 if (callingPackage != null) {
4660 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004661 if (pi != null && pi.restrictedAccountType != null) {
4662 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07004663 }
4664 } else {
4665 // Otherwise check if the callingUid has a package that has opted in
4666 for (String packageName : packages) {
4667 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
4668 if (pi != null && pi.restrictedAccountType != null) {
4669 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07004670 break;
4671 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004672 }
4673 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004674 } catch (NameNotFoundException nnfe) {
4675 }
4676 for (Account account : unfiltered) {
4677 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004678 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07004679 } else {
4680 boolean found = false;
4681 for (Account shared : sharedAccounts) {
4682 if (shared.equals(account)) {
4683 found = true;
4684 break;
4685 }
4686 }
4687 if (!found) {
4688 allowed.add(account);
4689 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004690 }
4691 }
4692 Account[] filtered = new Account[allowed.size()];
4693 allowed.toArray(filtered);
4694 return filtered;
4695 } else {
4696 return unfiltered;
4697 }
4698 }
4699
Amith Yamasani27db4682013-03-30 17:07:47 -07004700 /*
4701 * packageName can be null. If not null, it should be used to filter out restricted accounts
4702 * that the package is not allowed to access.
4703 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004704 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07004705 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004706 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004707 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004708 if (accounts == null) {
4709 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08004710 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004711 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07004712 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08004713 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004714 } else {
4715 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004716 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004717 totalLength += accounts.length;
4718 }
4719 if (totalLength == 0) {
4720 return EMPTY_ACCOUNT_ARRAY;
4721 }
4722 Account[] accounts = new Account[totalLength];
4723 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004724 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004725 System.arraycopy(accountsOfType, 0, accounts, totalLength,
4726 accountsOfType.length);
4727 totalLength += accountsOfType.length;
4728 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004729 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08004730 }
4731 }
4732
Amith Yamasani04e0d262012-02-14 11:50:53 -08004733 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4734 Account account, String key, String value) {
4735 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004736 if (userDataForAccount == null) {
4737 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004738 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004739 }
4740 if (value == null) {
4741 userDataForAccount.remove(key);
4742 } else {
4743 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08004744 }
4745 }
4746
Carlos Valdivia91979be2015-05-22 14:11:35 -07004747 protected String readCachedTokenInternal(
4748 UserAccounts accounts,
4749 Account account,
4750 String tokenType,
4751 String callingPackage,
4752 byte[] pkgSigDigest) {
4753 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004754 return accounts.accountTokenCaches.get(
4755 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004756 }
4757 }
4758
Amith Yamasani04e0d262012-02-14 11:50:53 -08004759 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
4760 Account account, String key, String value) {
4761 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004762 if (authTokensForAccount == null) {
4763 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004764 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004765 }
4766 if (value == null) {
4767 authTokensForAccount.remove(key);
4768 } else {
4769 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08004770 }
4771 }
4772
Amith Yamasani04e0d262012-02-14 11:50:53 -08004773 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
4774 String authTokenType) {
4775 synchronized (accounts.cacheLock) {
4776 HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08004777 if (authTokensForAccount == null) {
4778 // need to populate the cache for this account
Amith Yamasani04e0d262012-02-14 11:50:53 -08004779 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004780 authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004781 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08004782 }
4783 return authTokensForAccount.get(authTokenType);
4784 }
4785 }
4786
Simranjit Kohli27d0e1f2016-02-25 21:15:02 +00004787 protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
4788 synchronized (accounts.cacheLock) {
4789 HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
4790 if (userDataForAccount == null) {
4791 // need to populate the cache for this account
4792 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
4793 userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
4794 accounts.userDataCache.put(account, userDataForAccount);
4795 }
4796 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08004797 }
4798 }
4799
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004800 protected HashMap<String, String> readUserDataForAccountFromDatabaseLocked(
4801 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08004802 HashMap<String, String> userDataForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08004803 Cursor cursor = db.query(TABLE_EXTRAS,
4804 COLUMNS_EXTRAS_KEY_AND_VALUE,
4805 SELECTION_USERDATA_BY_ACCOUNT,
4806 new String[]{account.name, account.type},
4807 null, null, null);
4808 try {
4809 while (cursor.moveToNext()) {
4810 final String tmpkey = cursor.getString(0);
4811 final String value = cursor.getString(1);
4812 userDataForAccount.put(tmpkey, value);
4813 }
4814 } finally {
4815 cursor.close();
4816 }
4817 return userDataForAccount;
4818 }
4819
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004820 protected HashMap<String, String> readAuthTokensForAccountFromDatabaseLocked(
4821 final SQLiteDatabase db, Account account) {
Fred Quintana56285a62010-12-02 14:20:51 -08004822 HashMap<String, String> authTokensForAccount = new HashMap<String, String>();
Fred Quintana56285a62010-12-02 14:20:51 -08004823 Cursor cursor = db.query(TABLE_AUTHTOKENS,
4824 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
4825 SELECTION_AUTHTOKENS_BY_ACCOUNT,
4826 new String[]{account.name, account.type},
4827 null, null, null);
4828 try {
4829 while (cursor.moveToNext()) {
4830 final String type = cursor.getString(0);
4831 final String authToken = cursor.getString(1);
4832 authTokensForAccount.put(type, authToken);
4833 }
4834 } finally {
4835 cursor.close();
4836 }
4837 return authTokensForAccount;
4838 }
Kenny Guy07ad8dc2014-09-01 20:56:12 +01004839
4840 private Context getContextForUser(UserHandle user) {
4841 try {
4842 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
4843 } catch (NameNotFoundException e) {
4844 // Default to mContext, not finding the package system is running as is unlikely.
4845 return mContext;
4846 }
4847 }
Sandra Kwan78812282015-11-04 11:19:47 -08004848
4849 private void sendResponse(IAccountManagerResponse response, Bundle result) {
4850 try {
4851 response.onResult(result);
4852 } catch (RemoteException e) {
4853 // if the caller is dead then there is no one to care about remote
4854 // exceptions
4855 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4856 Log.v(TAG, "failure while notifying response", e);
4857 }
4858 }
4859 }
4860
4861 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
4862 String errorMessage) {
4863 try {
4864 response.onError(errorCode, errorMessage);
4865 } catch (RemoteException e) {
4866 // if the caller is dead then there is no one to care about remote
4867 // exceptions
4868 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4869 Log.v(TAG, "failure while notifying response", e);
4870 }
4871 }
4872 }
Fred Quintana60307342009-03-24 22:48:12 -07004873}