blob: 66e61f8904cc9482c929ac1b8663e2284427938a [file] [log] [blame]
Fred Quintana60307342009-03-24 22:48:12 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.accounts;
Fred Quintana60307342009-03-24 22:48:12 -070018
Doug Zongker885cfc232009-10-21 16:52:44 -070019import android.Manifest;
Carlos Valdivia91979be2015-05-22 14:11:35 -070020import android.accounts.AbstractAccountAuthenticator;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080021import android.accounts.Account;
22import android.accounts.AccountAndUser;
23import android.accounts.AccountAuthenticatorResponse;
24import android.accounts.AccountManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070025import android.accounts.AccountManagerInternal;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080026import android.accounts.AuthenticatorDescription;
Amith Yamasani23c8b962013-04-10 13:37:18 -070027import android.accounts.CantAddAccountActivity;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080028import android.accounts.GrantCredentialsPermissionActivity;
Svet Ganovf6d424f12016-09-20 20:18:53 -070029import android.accounts.IAccountAccessTracker;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080030import android.accounts.IAccountAuthenticator;
31import android.accounts.IAccountAuthenticatorResponse;
32import android.accounts.IAccountManager;
33import android.accounts.IAccountManagerResponse;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070034import android.annotation.IntRange;
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -070035import android.annotation.NonNull;
Svet Ganovf6d424f12016-09-20 20:18:53 -070036import android.annotation.Nullable;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080037import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070038import android.app.ActivityManagerNative;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070039import android.app.ActivityThread;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070040import android.app.AppGlobals;
Svetoslavf3f02ac2015-09-08 14:36:35 -070041import android.app.AppOpsManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070042import android.app.INotificationManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070043import android.app.Notification;
44import android.app.NotificationManager;
45import android.app.PendingIntent;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000046import android.app.admin.DeviceAdminInfo;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010047import android.app.admin.DevicePolicyManager;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000048import android.app.admin.DevicePolicyManagerInternal;
Fred Quintanaa698f422009-04-08 19:14:54 -070049import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070050import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070051import android.content.ContentValues;
52import android.content.Context;
53import android.content.Intent;
54import android.content.IntentFilter;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070055import android.content.IntentSender;
Fred Quintanab839afc2009-10-14 15:57:28 -070056import android.content.ServiceConnection;
Carlos Valdivia6ede9c32016-03-10 20:12:32 -080057import android.content.pm.ActivityInfo;
Doug Zongker885cfc232009-10-21 16:52:44 -070058import android.content.pm.ApplicationInfo;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070059import android.content.pm.IPackageManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070060import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070061import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070062import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070063import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070064import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070065import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070066import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070067import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070068import android.database.Cursor;
69import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070070import android.database.sqlite.SQLiteDatabase;
71import android.database.sqlite.SQLiteOpenHelper;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070072import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070073import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070074import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080075import android.os.Environment;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070076import android.os.FileUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070077import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070078import android.os.IBinder;
79import android.os.Looper;
80import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070081import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070082import android.os.Process;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070083import android.os.RemoteCallback;
Fred Quintanaa698f422009-04-08 19:14:54 -070084import android.os.RemoteException;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070085import android.os.ServiceManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070086import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070087import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070088import android.os.UserManager;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070089import android.os.storage.StorageManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070090import android.text.TextUtils;
91import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070092import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070093import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080094import android.util.SparseArray;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070095import android.util.SparseBooleanArray;
Fred Quintana60307342009-03-24 22:48:12 -070096
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070097import com.android.internal.R;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -070098import com.android.internal.annotations.VisibleForTesting;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070099import com.android.internal.content.PackageMonitor;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800100import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800101import com.android.internal.util.IndentingPrintWriter;
Fyodor Kupolov35f68082016-04-06 12:14:17 -0700102import com.android.internal.util.Preconditions;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +0000103import com.android.server.LocalServices;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700104import com.android.server.ServiceThread;
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600105import com.android.server.SystemService;
106
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700107import com.google.android.collect.Lists;
108import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700109
Oscar Montemayora8529f62009-11-18 10:14:20 -0800110import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -0700111import java.io.FileDescriptor;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700112import java.io.IOException;
Fred Quintanaa698f422009-04-08 19:14:54 -0700113import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -0800114import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -0700115import java.security.MessageDigest;
116import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700117import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -0700118import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -0800119import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700120import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700121import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700122import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700123import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -0800124import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700125import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800126import java.util.Map;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800127import java.util.Map.Entry;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700128import java.util.Set;
Svet Ganovf6d424f12016-09-20 20:18:53 -0700129import java.util.concurrent.CopyOnWriteArrayList;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700130import java.util.concurrent.atomic.AtomicInteger;
131import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700132
Fred Quintana60307342009-03-24 22:48:12 -0700133/**
134 * A system service that provides account, password, and authtoken management for all
135 * accounts on the device. Some of these calls are implemented with the help of the corresponding
136 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
137 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700138 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700139 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700140 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700141public class AccountManagerService
142 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800143 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -0700144 private static final String TAG = "AccountManagerService";
145
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600146 public static class Lifecycle extends SystemService {
147 private AccountManagerService mService;
148
149 public Lifecycle(Context context) {
150 super(context);
151 }
152
153 @Override
154 public void onStart() {
155 mService = new AccountManagerService(getContext());
156 publishBinderService(Context.ACCOUNT_SERVICE, mService);
157 }
158
159 @Override
160 public void onBootPhase(int phase) {
161 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
162 mService.systemReady();
163 }
164 }
165
166 @Override
167 public void onUnlockUser(int userHandle) {
168 mService.onUnlockUser(userHandle);
169 }
170 }
171
Fred Quintana60307342009-03-24 22:48:12 -0700172 private static final String DATABASE_NAME = "accounts.db";
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700173 private static final int PRE_N_DATABASE_VERSION = 9;
174 private static final int CE_DATABASE_VERSION = 10;
175 private static final int DE_DATABASE_VERSION = 1;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700176
177 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700178
179 private final Context mContext;
180
Fred Quintana56285a62010-12-02 14:20:51 -0800181 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700182 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700183 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -0800184
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700185 private final MessageHandler mHandler;
Tejas Khorana7b88f0e2016-06-13 13:06:35 -0700186
Fred Quintana60307342009-03-24 22:48:12 -0700187 // Messages that can be sent on mHandler
188 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700189 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700190
Fred Quintana56285a62010-12-02 14:20:51 -0800191 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700192
193 private static final String TABLE_ACCOUNTS = "accounts";
194 private static final String ACCOUNTS_ID = "_id";
195 private static final String ACCOUNTS_NAME = "name";
196 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700197 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700198 private static final String ACCOUNTS_PASSWORD = "password";
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700199 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800200 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
201 "last_password_entry_time_millis_epoch";
Fred Quintana60307342009-03-24 22:48:12 -0700202
203 private static final String TABLE_AUTHTOKENS = "authtokens";
204 private static final String AUTHTOKENS_ID = "_id";
205 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
206 private static final String AUTHTOKENS_TYPE = "type";
207 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
208
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700209 private static final String TABLE_GRANTS = "grants";
210 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
211 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
212 private static final String GRANTS_GRANTEE_UID = "uid";
213
Fred Quintana60307342009-03-24 22:48:12 -0700214 private static final String TABLE_EXTRAS = "extras";
215 private static final String EXTRAS_ID = "_id";
216 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
217 private static final String EXTRAS_KEY = "key";
218 private static final String EXTRAS_VALUE = "value";
219
220 private static final String TABLE_META = "meta";
221 private static final String META_KEY = "key";
222 private static final String META_VALUE = "value";
223
Amith Yamasani67df64b2012-12-14 12:09:36 -0800224 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700225 private static final String SHARED_ACCOUNTS_ID = "_id";
226
227 private static final String PRE_N_DATABASE_NAME = "accounts.db";
228 private static final String CE_DATABASE_NAME = "accounts_ce.db";
229 private static final String DE_DATABASE_NAME = "accounts_de.db";
230 private static final String CE_DB_PREFIX = "ceDb.";
231 private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS;
232 private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
233 private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800234
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700235 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
236 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700237 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800238
Carlos Valdivia91979be2015-05-22 14:11:35 -0700239 static {
240 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
241 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
242 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700243
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700244 private static final String COUNT_OF_MATCHING_GRANTS = ""
245 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
246 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
247 + " AND " + GRANTS_GRANTEE_UID + "=?"
248 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
249 + " AND " + ACCOUNTS_NAME + "=?"
250 + " AND " + ACCOUNTS_TYPE + "=?";
251
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700252 private static final String COUNT_OF_MATCHING_GRANTS_ANY_TOKEN = ""
253 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
254 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
255 + " AND " + GRANTS_GRANTEE_UID + "=?"
256 + " AND " + ACCOUNTS_NAME + "=?"
257 + " AND " + ACCOUNTS_TYPE + "=?";
258
Fred Quintana56285a62010-12-02 14:20:51 -0800259 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
260 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
Carlos Valdivia91979be2015-05-22 14:11:35 -0700261
Fred Quintana56285a62010-12-02 14:20:51 -0800262 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
263 AUTHTOKENS_AUTHTOKEN};
264
265 private static final String SELECTION_USERDATA_BY_ACCOUNT =
266 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
267 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
268
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800269 private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX =
270 "auth_uid_for_type:";
271 private static final String META_KEY_DELIMITER = ":";
272 private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
273
Fred Quintanaa698f422009-04-08 19:14:54 -0700274 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700275 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
276
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700277 private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
278
Amith Yamasani04e0d262012-02-14 11:50:53 -0800279 static class UserAccounts {
280 private final int userId;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700281 private final DeDatabaseHelper openHelper;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800282 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
283 credentialsPermissionNotificationIds =
284 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
285 private final HashMap<Account, Integer> signinRequiredNotificationIds =
286 new HashMap<Account, Integer>();
287 private final Object cacheLock = new Object();
288 /** protected by the {@link #cacheLock} */
Amith Yamasanib483a992012-05-22 13:14:25 -0700289 private final HashMap<String, Account[]> accountCache =
Svet Ganovf6d424f12016-09-20 20:18:53 -0700290 new LinkedHashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800291 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700292 private final Map<Account, Map<String, String>> userDataCache = new HashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800293 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700294 private final Map<Account, Map<String, String>> authTokenCache = new HashMap<>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700295
296 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700297 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700298
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700299 /** protected by the {@link #cacheLock} */
300 private final Map<String, ArrayList<Integer>> mApplicationAccountRequestMappings =
301 new HashMap<>();
302
303 /* Together the below two Sparse Arrays serve as visible list. One maps UID to account
304 number. Another maps Account number to Account.*/
305
306 /** protected by the {@link #cacheLock} */
307 private final SparseArray<ArrayList<Integer>> mVisibleListUidToMockAccountNumbers =
308 new SparseArray<>();
309
310 //TODO: Instead of using Mock Account IDs, use the actual account IDs.
311 /** protected by the {@link #cacheLock} */
312 private final SparseArray<Account> mMockAccountIdToAccount = new SparseArray<>();
313
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700314 /**
315 * protected by the {@link #cacheLock}
316 *
317 * Caches the previous names associated with an account. Previous names
318 * should be cached because we expect that when an Account is renamed,
319 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
320 * want to know if the accounts they care about have been renamed.
321 *
322 * The previous names are wrapped in an {@link AtomicReference} so that
323 * we can distinguish between those accounts with no previous names and
324 * those whose previous names haven't been cached (yet).
325 */
326 private final HashMap<Account, AtomicReference<String>> previousNameCache =
327 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800328
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700329 private int debugDbInsertionPoint = -1;
330 private SQLiteStatement statementForLogging;
331
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700332 UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800333 this.userId = userId;
334 synchronized (cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700335 openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800336 }
337 }
338 }
339
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700340 private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600341 private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
Svet Ganovf6d424f12016-09-20 20:18:53 -0700342 private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
343 mAppPermissionChangeListeners = new CopyOnWriteArrayList<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800344
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700345 private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
Fred Quintana31957f12009-10-21 13:43:10 -0700346 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700347
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700348 /**
349 * This should only be called by system code. One should only call this after the service
350 * has started.
351 * @return a reference to the AccountManagerService instance
352 * @hide
353 */
354 public static AccountManagerService getSingleton() {
355 return sThis.get();
356 }
Fred Quintana60307342009-03-24 22:48:12 -0700357
Fred Quintana56285a62010-12-02 14:20:51 -0800358 public AccountManagerService(Context context) {
359 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700360 }
361
Fred Quintana56285a62010-12-02 14:20:51 -0800362 public AccountManagerService(Context context, PackageManager packageManager,
363 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700364 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800365 mPackageManager = packageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700366 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fred Quintana60307342009-03-24 22:48:12 -0700367
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700368 ServiceThread serviceThread = new ServiceThread(TAG,
369 android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
370 serviceThread.start();
371 mHandler = new MessageHandler(serviceThread.getLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700372
Fred Quintana56285a62010-12-02 14:20:51 -0800373 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800374 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700375
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700376 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800377
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700378 addRequestsForPreInstalledApplications();
379
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800380 IntentFilter intentFilter = new IntentFilter();
381 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
382 intentFilter.addDataScheme("package");
383 mContext.registerReceiver(new BroadcastReceiver() {
384 @Override
385 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700386 // Don't delete accounts when updating a authenticator's
387 // package.
388 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700389 /* Purging data requires file io, don't block the main thread. This is probably
390 * less than ideal because we are introducing a race condition where old grants
391 * could be exercised until they are purged. But that race condition existed
392 * anyway with the broadcast receiver.
393 *
394 * Ideally, we would completely clear the cache, purge data from the database,
395 * and then rebuild the cache. All under the cache lock. But that change is too
396 * large at this point.
397 */
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700398 Runnable purgingRunnable = new Runnable() {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700399 @Override
400 public void run() {
401 purgeOldGrantsAll();
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700402
403 /* clears application request's for account types supported */
404 int uidOfUninstalledApplication =
405 intent.getIntExtra(Intent.EXTRA_UID, -1);
406 if(uidOfUninstalledApplication != -1) {
407 clearRequestedAccountVisibility(uidOfUninstalledApplication,
408 getUserAccounts(UserHandle.getUserId(
409 uidOfUninstalledApplication)));
410 }
411
412 /* removes visibility of previous UID of this uninstalled application*/
413 removeAccountVisibilityAllAccounts(uidOfUninstalledApplication,
414 getUserAccounts(UserHandle.getUserId(
415 uidOfUninstalledApplication)));
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700416 }
417 };
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700418 mHandler.post(purgingRunnable);
Carlos Valdivia23f58262014-09-05 10:52:41 -0700419 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700420
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800421 }
422 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800423
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700424 IntentFilter packageAddedOrChangedFilter = new IntentFilter();
425 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
426 packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
427 packageAddedOrChangedFilter.addDataScheme("package");
428 mContext.registerReceiverAsUser(new BroadcastReceiver() {
429 @Override
430 public void onReceive(Context context1, Intent intent) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700431 mHandler.post(new Runnable() {
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700432 @Override
433 public void run() {
434 int uidOfInstalledApplication =
435 intent.getIntExtra(Intent.EXTRA_UID, -1);
436 if(uidOfInstalledApplication != -1) {
437 registerAccountTypesSupported(
438 uidOfInstalledApplication,
439 getUserAccounts(
440 UserHandle.getUserId(uidOfInstalledApplication)));
441 }
442 }
443 });
444 }
445 }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
446
Amith Yamasani13593602012-03-22 16:16:17 -0700447 IntentFilter userFilter = new IntentFilter();
448 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800449 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700450 @Override
451 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800452 String action = intent.getAction();
453 if (Intent.ACTION_USER_REMOVED.equals(action)) {
454 onUserRemoved(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800455 }
Amith Yamasani13593602012-03-22 16:16:17 -0700456 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800457 }, UserHandle.ALL, userFilter, null, null);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700458
459 LocalServices.addService(AccountManagerInternal.class, new AccountManagerInternalImpl());
460
461 // Need to cancel account request notifications if the update/install can access the account
462 new PackageMonitor() {
463 @Override
464 public void onPackageAdded(String packageName, int uid) {
465 // Called on a handler, and running as the system
466 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
467 }
468
469 @Override
470 public void onPackageUpdateFinished(String packageName, int uid) {
471 // Called on a handler, and running as the system
472 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
473 }
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700474 }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700475
476 // Cancel account request notification if an app op was preventing the account access
477 mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
478 new AppOpsManager.OnOpChangedInternalListener() {
479 @Override
480 public void onOpChanged(int op, String packageName) {
481 try {
482 final int userId = ActivityManager.getCurrentUser();
483 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
484 final int mode = mAppOpsManager.checkOpNoThrow(
485 AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
486 if (mode == AppOpsManager.MODE_ALLOWED) {
487 final long identity = Binder.clearCallingIdentity();
488 try {
489 cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
490 } finally {
491 Binder.restoreCallingIdentity(identity);
492 }
493 }
494 } catch (NameNotFoundException e) {
495 /* ignore */
496 }
497 }
498 });
499
500 // Cancel account request notification if a permission was preventing the account access
501 mPackageManager.addOnPermissionsChangeListener(
502 (int uid) -> {
503 Account[] accounts = null;
504 String[] packageNames = mPackageManager.getPackagesForUid(uid);
505 if (packageNames != null) {
506 final int userId = UserHandle.getUserId(uid);
507 final long identity = Binder.clearCallingIdentity();
508 try {
509 for (String packageName : packageNames) {
510 if (mContext.getPackageManager().checkPermission(
511 Manifest.permission.GET_ACCOUNTS, packageName)
512 != PackageManager.PERMISSION_GRANTED) {
513 continue;
514 }
515
516 if (accounts == null) {
517 accounts = getAccountsAsUser(null, userId, "android");
518 if (ArrayUtils.isEmpty(accounts)) {
519 return;
520 }
521 }
522
523 for (Account account : accounts) {
524 cancelAccountAccessRequestNotificationIfNeeded(
525 account, uid, packageName, true);
526 }
527 }
528 } finally {
529 Binder.restoreCallingIdentity(identity);
530 }
531 }
532 });
533 }
534
535 private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
536 boolean checkAccess) {
537 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
538 for (Account account : accounts) {
539 cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
540 }
541 }
542
543 private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
544 boolean checkAccess) {
545 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
546 for (Account account : accounts) {
547 cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
548 }
549 }
550
551 private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
552 boolean checkAccess) {
553 String[] packageNames = mPackageManager.getPackagesForUid(uid);
554 if (packageNames != null) {
555 for (String packageName : packageNames) {
556 cancelAccountAccessRequestNotificationIfNeeded(account, uid,
557 packageName, checkAccess);
558 }
559 }
560 }
561
562 private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
563 int uid, String packageName, boolean checkAccess) {
564 if (!checkAccess || hasAccountAccess(account, packageName,
565 UserHandle.getUserHandleForUid(uid))) {
566 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -0700567 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700568 UserHandle.getUserHandleForUid(uid));
569 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800570 }
571
Dianne Hackborn164371f2013-10-01 19:10:13 -0700572 @Override
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700573 public boolean addAccountExplicitlyWithUid(Account account, String password, Bundle extras,
574 int[] selectedUids) {
575 if(addAccountExplicitly(account,password,extras)) {
576 for(int thisUid : selectedUids) {
577 makeAccountVisible(account, thisUid);
578 }
579 return true;
580 }
581 return false;
582 }
583
584 @Override
585 public int[] getRequestingUidsForType(String accountType) {
586 int callingUid = Binder.getCallingUid();
587 if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
588 String msg = String.format(
589 "uid %s cannot get secrets for accounts of type: %s",
590 callingUid,
591 accountType);
592 throw new SecurityException(msg);
593 }
594 return getRequestingUidsForType(accountType, getUserAccounts(
595 UserHandle.getUserId(callingUid)));
596 }
597
598 /**
599 * Returns all UIDs for applications that requested the account type. This method
600 * is called indirectly by the Authenticator and AccountManager
601 *
602 * @param accountType authenticator would like to know the requesting apps of
603 * @param ua UserAccount that currently hosts the account and application
604 *
605 * @return ArrayList of all UIDs that support accounts of this
606 * account type that seek approval (to be used to know which accounts for
607 * the authenticator to include in addAccountExplicitly). Null if none.
608 */
609 private int[] getRequestingUidsForType(String accountType, UserAccounts ua) {
610 synchronized(ua.cacheLock) {
611 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
612 ua.mApplicationAccountRequestMappings;
613 ArrayList<Integer> allUidsForAccountType = userApplicationAccountRequestMappings.get(
614 accountType);
615 if(allUidsForAccountType == null) {
616 return null;
617 }
618 int[] toReturn = new int[allUidsForAccountType.size()];
619 for(int i = 0 ; i < toReturn.length ; i++) {
620 toReturn[i] = allUidsForAccountType.get(i);
621 }
622 return toReturn;
623 }
624 }
625
626 @Override
627 public boolean isAccountVisible(Account a, int uid) {
628 int callingUid = Binder.getCallingUid();
629 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
630 String msg = String.format(
631 "uid %s cannot get secrets for accounts of type: %s",
632 callingUid,
633 a.type);
634 throw new SecurityException(msg);
635 }
636 return isAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
637 }
638
639 /**
640 * Checks visibility of certain account of a process identified
641 * by a given UID. This is called by the Authenticator indirectly.
642 *
643 * @param a The account to check visibility of
644 * @param uid UID to check visibility of
645 * @param ua UserAccount that currently hosts the account and application
646 *
647 * @return True if application has access to the account
648 *
649 */
650 private boolean isAccountVisible(Account a, int uid, UserAccounts ua) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700651 if(isAccountManagedByCaller(a.type, uid, UserHandle.getUserId(uid))) {
652 return true;
653 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700654 int accountMapping = getMockAccountNumber(a, ua);
655 if(accountMapping < 0) {
656 return true;
657 }
658 synchronized(ua.cacheLock) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700659 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700660 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
661 ua.mVisibleListUidToMockAccountNumbers;
662 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
Tejas Khorana69990d92016-08-03 11:19:40 -0700663 int indexOfAccountMapping = userAcctIdToAcctMap.indexOfValueByValue(a);
664 return indexOfAccountMapping == -1 || (linkedAccountsToUid != null
665 && linkedAccountsToUid.contains(accountMapping));
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700666 }
667 }
668
669 @Override
670 public boolean makeAccountVisible(Account a, int uid) {
671 int callingUid = Binder.getCallingUid();
672 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
673 String msg = String.format(
674 "uid %s cannot get secrets for accounts of type: %s",
675 callingUid,
676 a.type);
677 throw new SecurityException(msg);
678 }
679 return makeAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
680 }
681
682 /**
683 * Gives a certain UID, represented a application, access to an account. This method
684 * is called indirectly by the Authenticator.
685 *
686 * @param a Account to make visible
687 * @param uid to add visibility of the Account from
688 * @param ua UserAccount that currently hosts the account and application
689 *
690 * @return True if account made visible to application and was not previously visible.
691 */
692 private boolean makeAccountVisible(Account a, int uid, UserAccounts ua) {
693 int accountMapping = getMockAccountNumber(a, ua);
694 if(accountMapping < 0) {
695 accountMapping = makeAccountNumber(a, ua);
696 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700697 synchronized(ua.cacheLock) {
698 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
699 ua.mVisibleListUidToMockAccountNumbers;
700 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
701 if(linkedAccountsToUid == null) {
702 linkedAccountsToUid = new ArrayList<>();
703 linkedAccountsToUid.add(accountMapping);
704 userWlUidToMockAccountNums.put(uid, linkedAccountsToUid);
705 } else if(!linkedAccountsToUid.contains(accountMapping)) {
706 linkedAccountsToUid.add(accountMapping);
707 } else {
708 return false;
709 }
710 }
711
712 String[] subPackages = mPackageManager.getPackagesForUid(uid);
713 if(subPackages != null) {
714 for(String subPackage : subPackages) {
715 sendNotification(subPackage, a);
716 }
717 }
718 return true;
719 }
720
721 @Override
722 public boolean removeAccountVisibility(Account a, int uid) {
723 int callingUid = Binder.getCallingUid();
724 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
725 String msg = String.format(
726 "uid %s cannot get secrets for accounts of type: %s",
727 callingUid,
728 a.type);
729 throw new SecurityException(msg);
730 }
731 return removeAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
732 }
733
734 /**
735 * Removes visibility of certain account of a process identified
736 * by a given UID to an application. This is called directly by the
737 * AccountManager and indirectly by the Authenticator.
738 *
739 * @param a Account to remove visibility from
740 * @param uid UID to remove visibility of the Account from
741 * @param ua UserAccount that hosts the account and application
742 *
743 * @return True if application access to account removed and was previously visible.
744 */
745 private boolean removeAccountVisibility(Account a, int uid, UserAccounts ua) {
746 int accountMapping = getMockAccountNumber(a, ua);
747 if(accountMapping < 0) {
748 return false;
749 }
750 synchronized(ua.cacheLock) {
751 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
752 ua.mVisibleListUidToMockAccountNumbers;
753 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
754 if(linkedAccountsToUid != null) {
755 boolean toReturn = linkedAccountsToUid.remove((Integer) accountMapping);
756 if(linkedAccountsToUid.size() == 0) {
757 userWlUidToMockAccountNums.remove(uid);
758 }
759 return toReturn;
760 }
761 }
762 return false;
763 }
764
765 /**
766 * Registers an application's preferences for supported account types for login. This is
767 * a helper method of requestAccountVisibility and indirectly called by AccountManager.
768 *
769 * @param accountTypes account types third party app is willing to support
770 * @param uid of application requesting account visibility
771 * @param ua UserAccount that hosts the account and application
772 */
773 private void addRequestedAccountsVisibility(String[] accountTypes, int uid, UserAccounts ua) {
774 synchronized(ua.cacheLock) {
775 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
776 ua.mApplicationAccountRequestMappings;
777 for(String accountType : accountTypes) {
778 ArrayList<Integer> accountUidAppList = userApplicationAccountRequestMappings
779 .get(accountType);
780 if(accountUidAppList == null) {
781 accountUidAppList = new ArrayList<>();
782 accountUidAppList.add(uid);
783 userApplicationAccountRequestMappings.put(accountType, accountUidAppList);
784 } else if (!accountUidAppList.contains(uid)) {
785 accountUidAppList.add(uid);
786 }
787 }
788 }
789 }
790
791 /**
792 * Registers the requested login account types requested by all the applications already
793 * installed on the device.
794 */
795 private void addRequestsForPreInstalledApplications() {
796 List<PackageInfo> allInstalledPackages = mContext.getPackageManager().
797 getInstalledPackages(0);
798 for(PackageInfo pi : allInstalledPackages) {
799 int currentUid = pi.applicationInfo.uid;
800 if(currentUid != -1) {
801 registerAccountTypesSupported(currentUid,
802 getUserAccounts(UserHandle.getUserId(currentUid)));
803 }
804 }
805 }
806
807 /**
808 * Clears all preferences an application had for login account types it offered
809 * support for. This method is used by AccountManager after application is
810 * uninstalled.
811 *
812 * @param uid Uid of the application to clear account type preferences
813 * @param ua UserAccount that hosted the account and application
814 *
815 * @return true if any previous settings were overridden.
816 */
817 private boolean clearRequestedAccountVisibility(int uid, UserAccounts ua) {
818 boolean accountsDeleted = false;
819 ArrayList<String> accountTypesToRemove = new ArrayList<>();
820 synchronized(ua.cacheLock) {
821 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
822 ua.mApplicationAccountRequestMappings;
823 Set<Entry<String, ArrayList<Integer>>> accountTypeAppListEntries =
824 userApplicationAccountRequestMappings.entrySet();
825
826 for(Entry<String, ArrayList<Integer>> entry : accountTypeAppListEntries) {
827 ArrayList<Integer> supportedApps = entry.getValue();
828 if(supportedApps.remove((Integer) uid)) {
829 accountsDeleted = true;
830 }
831
832 if(supportedApps.isEmpty()) {
833 accountTypesToRemove.add(entry.getKey());
834 }
835 }
836
837 for(String s : accountTypesToRemove) {
838 userApplicationAccountRequestMappings.remove(s);
839 }
840 }
841
842 return accountsDeleted;
843 }
844
845 /**
846 * Retrieves the mock account number associated with an Account in order to later retrieve
847 * the account from the Integer-Account Mapping. An account number is not the same as
848 * accountId in the database. This method can be indirectly called by AccountManager and
849 * indirectly by the Authenticator.
850 *
851 * @param a account to retrieve account number mapping
852 * @param ua UserAccount that currently hosts the account and application
853 *
854 * @return account number affiliated with the Account in question. Negative number if none.
855 */
856 private int getMockAccountNumber(Account a, UserAccounts ua) {
857 //TODO: Each account is linked to AccountId rather than generated mock account numbers
858 SparseArray<Account> userAcctIdToAcctMap =
859 ua.mMockAccountIdToAccount;
860 synchronized(ua.cacheLock) {
861 int indexOfAccount = userAcctIdToAcctMap.indexOfValueByValue(a);
862 if(indexOfAccount < 0) {
863 return -1;
864 }
865 return userAcctIdToAcctMap.keyAt(indexOfAccount);
866 }
867 }
868
869 /**
870 * Returns a full list of accounts that a certain UID is allowed access
871 * based on the visible list entries.
872 *
873 * @param uid of application to retrieve visible listed accounts for
874 * @param ua UserAccount that currently hosts the account and application
875 *
876 * @return array of Account values that are accessible by the given uids
877 */
878 private Account[] getVisibleListedAccounts(int uid, UserAccounts ua) {
879 ArrayList<Account> visibleListedAccounts = new ArrayList<>();
880 synchronized(ua.cacheLock) {
881 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
882 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
883 ua.mVisibleListUidToMockAccountNumbers;
884 ArrayList<Integer> visibleListedUidAccountNumbers =
885 userWlUidToMockAccountNums.get(uid);
886 if(visibleListedUidAccountNumbers != null) {
887 for(Integer accountNumber : visibleListedUidAccountNumbers) {
888 Account currentAccount = userAcctIdToAcctMap.get(accountNumber);
889 visibleListedAccounts.add(currentAccount);
890 }
891 }
892 }
893 Account[] arrVisibleListedAccounts = new Account[visibleListedAccounts.size()];
894 return visibleListedAccounts.toArray(arrVisibleListedAccounts);
895 }
896
897 /**
898 * Makes an account number for a given Account to be mapped to.
899 * This method is called by makeVisible if an Account does not have
900 * a mapping for the visible list. This method is thus indirectly
901 * called by the Authenticator.
902 *
903 * @param a account to make an account number mapping of
904 * @param ua UserAccount that currently hosts the account and application
905 *
906 * @return account number created to map to the given account
907 */
908 // TODO: Remove this method and use accountId from DB.
909 private int makeAccountNumber(Account a, UserAccounts ua) {
910 synchronized(ua.cacheLock) {
911 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
912 int newAccountMapping = 0;
913 while(userAcctIdToAcctMap.get(newAccountMapping) != null) {
914 newAccountMapping++;
915 }
916 userAcctIdToAcctMap.put(newAccountMapping, a);
917 return newAccountMapping;
918 }
919 }
920
921
922
923 /**
924 * Registers an application, represented by a UID, to support account types detailed in
925 * the applications manifest as well as allowing it to opt for notifications.
926 *
927 * @param uid UID of application
928 * @param ua UserAccount that currently hosts the account and application
929 */
930 private void registerAccountTypesSupported(int uid, UserAccounts ua) {
931 /* Account types supported are drawn from the Android Manifest of the Application */
932 String interestedPackages = null;
933 try {
934 String[] allPackages = mPackageManager.getPackagesForUid(uid);
Nicolas Prevotf7d8df12016-09-16 17:45:34 +0100935 if (allPackages != null) {
936 for(String aPackage : allPackages) {
937 ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
938 PackageManager.GET_META_DATA);
939 Bundle b = ai.metaData;
940 if(b == null) {
941 return;
942 }
943 interestedPackages = b.getString("android.accounts.SupportedLoginTypes");
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700944 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700945 }
946 } catch (PackageManager.NameNotFoundException e) {
947 Log.d("NameNotFoundException", e.getMessage());
948 }
949 if(interestedPackages != null) {
950 /* request remote account types directly from here. Reads from Android Manifest */
951 requestAccountVisibility(interestedPackages.split(";"), uid, ua);
952 }
953 }
954
955 /**
956 * Allows AccountManager to register account types that an application has login
957 * support for. This method over-writes all of the application's previous settings
958 * for accounts it supported.
959 *
960 * @param accountTypes array of account types application wishes to support
961 * @param uid of application registering requested account types
962 * @param ua UserAccount that hosts the account and application
963 */
964 private void requestAccountVisibility(String[] accountTypes, int uid, UserAccounts ua) {
965 if(accountTypes.length > 0) {
966 clearRequestedAccountVisibility(uid, ua);
967 addRequestedAccountsVisibility(accountTypes, uid, ua);
968 }
969 }
970
971 /**
972 * Removes visibility of all Accounts to this particular UID. This is called when an
973 * application is uninstalled so another application that is installed with the same
974 * UID cannot access Accounts. This is called by AccountManager.
975 *
976 * @param uid of application to remove all Account visibility to
977 * @param ua UserAccount that hosts the current Account
978 */
979 private void removeAccountVisibilityAllAccounts(int uid, UserAccounts ua) {
980 synchronized(ua.cacheLock) {
981 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
982 ua.mVisibleListUidToMockAccountNumbers;
983 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
984 ArrayList<Integer> allAccountNumbersList = userWlUidToMockAccountNums.get(uid);
985 if(allAccountNumbersList != null) {
986 Integer[] allAccountNumbers = allAccountNumbersList.toArray(
987 new Integer[allAccountNumbersList.size()]);
988 for(int accountNum : allAccountNumbers) {
989 removeAccountVisibility(userAcctIdToAcctMap.get(accountNum), uid, ua);
990 }
991 }
992 }
993 }
994
995 /**
996 * Removes visible list functionality of a certain Account.
997 * This method is currently called by (1) addAccountExplicitly (as opposed to
998 * addAccountExplicitlyWithUid) and (2) removeAccountExplicitly.
999 *
1000 * @param a the account to clear the visible list functionality for
1001 * @param ua currently UserAccounts profile containing Account
1002 *
1003 * @return true if account previously had visible list functionality
1004 */
1005 private boolean removeVisibleListFunctionality(Account a, UserAccounts ua) {
1006 int mockAccountNum = getMockAccountNumber(a, ua);
1007 if(mockAccountNum < 0) {
1008 return false;
1009 }
1010 synchronized(ua.cacheLock) {
1011 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
1012 ua.mVisibleListUidToMockAccountNumbers;
1013 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
1014
1015 /* Removing mapping from account number to account removes visible list functionality*/
1016 userAcctIdToAcctMap.remove(mockAccountNum);
1017
1018 for(int i = userWlUidToMockAccountNums.size() - 1 ; i >= 0 ; i--) {
1019 int uidKey = userWlUidToMockAccountNums.keyAt(i);
1020 ArrayList<Integer> allAccountNumbers = userWlUidToMockAccountNums.get(uidKey);
1021 if(allAccountNumbers != null) {
1022 allAccountNumbers.remove(mockAccountNum);
1023 if(allAccountNumbers.isEmpty()) {
1024 userWlUidToMockAccountNums.remove(uidKey);
1025 }
1026 }
1027 }
1028 }
1029 return true;
1030 }
1031
1032 /**
1033 * Sends a direct intent to a package, notifying it of a visible account. This
1034 * method is a helper method of makeAccountVisible.
1035 *
1036 * @param desiredPackage to send Account to
1037 * @param visibleAccount to send to package
1038 */
1039 private void sendNotification(String desiredPackage, Account visibleAccount) {
1040 Intent intent = new Intent();
1041 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
1042 intent.setAction(NEW_ACCOUNT_VISIBLE);
1043 intent.setPackage(desiredPackage);
1044 intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
1045 mContext.sendBroadcast(intent);
1046 }
1047
1048 @Override
Dianne Hackborn164371f2013-10-01 19:10:13 -07001049 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
1050 throws RemoteException {
1051 try {
1052 return super.onTransact(code, data, reply, flags);
1053 } catch (RuntimeException e) {
1054 // The account manager only throws security exceptions, so let's
1055 // log all others.
1056 if (!(e instanceof SecurityException)) {
1057 Slog.wtf(TAG, "Account Manager Crash", e);
1058 }
1059 throw e;
1060 }
1061 }
1062
Kenny Root26ff6622012-07-30 12:58:03 -07001063 public void systemReady() {
Kenny Root26ff6622012-07-30 12:58:03 -07001064 }
1065
Amith Yamasani258848d2012-08-10 17:06:33 -07001066 private UserManager getUserManager() {
1067 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001068 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -07001069 }
1070 return mUserManager;
1071 }
1072
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001073 /**
1074 * Validate internal set of accounts against installed authenticators for
1075 * given user. Clears cached authenticators before validating.
1076 */
1077 public void validateAccounts(int userId) {
1078 final UserAccounts accounts = getUserAccounts(userId);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001079 // Invalidate user-specific cache to make sure we catch any
1080 // removed authenticators.
1081 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
1082 }
1083
1084 /**
1085 * Validate internal set of accounts against installed authenticators for
1086 * given user. Clear cached authenticators before validating when requested.
1087 */
1088 private void validateAccountsInternal(
1089 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001090 if (Log.isLoggable(TAG, Log.DEBUG)) {
1091 Log.d(TAG, "validateAccountsInternal " + accounts.userId
1092 + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001093 + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001094 }
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001095
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001096 if (invalidateAuthenticatorCache) {
1097 mAuthenticatorCache.invalidateCache(accounts.userId);
1098 }
1099
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001100 final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
1101 mAuthenticatorCache, accounts.userId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001102 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001103
Amith Yamasani04e0d262012-02-14 11:50:53 -08001104 synchronized (accounts.cacheLock) {
1105 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001106 boolean accountDeleted = false;
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001107
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001108 // Get a map of stored authenticator types to UID
1109 Map<String, Integer> metaAuthUid = AccountsDbUtils.findMetaAuthUid(db);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001110 // Create a list of authenticator type whose previous uid no longer exists
1111 HashSet<String> obsoleteAuthType = Sets.newHashSet();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001112 SparseBooleanArray knownUids = null;
1113 for (Entry<String, Integer> authToUidEntry : metaAuthUid.entrySet()) {
1114 String type = authToUidEntry.getKey();
1115 int uid = authToUidEntry.getValue();
1116 Integer knownUid = knownAuth.get(type);
1117 if (knownUid != null && uid == knownUid) {
1118 // Remove it from the knownAuth list if it's unchanged.
1119 knownAuth.remove(type);
1120 } else {
1121 /*
1122 * The authenticator is presently not cached and should only be triggered
1123 * when we think an authenticator has been removed (or is being updated).
1124 * But we still want to check if any data with the associated uid is
1125 * around. This is an (imperfect) signal that the package may be updating.
1126 *
1127 * A side effect of this is that an authenticator sharing a uid with
1128 * multiple apps won't get its credentials wiped as long as some app with
1129 * that uid is still on the device. But I suspect that this is a rare case.
1130 * And it isn't clear to me how an attacker could really exploit that
1131 * feature.
1132 *
1133 * The upshot is that we don't have to worry about accounts getting
1134 * uninstalled while the authenticator's package is being updated.
1135 *
1136 */
1137 if (knownUids == null) {
1138 knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001139 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001140 if (!knownUids.get(uid)) {
1141 // The authenticator is not presently available to the cache. And the
1142 // package no longer has a data directory (so we surmise it isn't updating).
1143 // So purge its data from the account databases.
1144 obsoleteAuthType.add(type);
1145 // And delete it from the TABLE_META
1146 AccountsDbUtils.deleteMetaByAuthTypeAndUid(db, type, uid);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001147 }
1148 }
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001149 }
1150
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001151 // Add the newly registered authenticator to TABLE_META. If old authenticators have
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001152 // been re-enabled (after being updated for example), then we just overwrite the old
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001153 // values.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001154 for (Entry<String, Integer> entry : knownAuth.entrySet()) {
1155 AccountsDbUtils.insertOrReplaceMetaAuthTypeAndUid(db, entry.getKey(),
1156 entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001157 }
1158
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001159 final Map<Long, Account> accountsMap = AccountsDbUtils.findAllAccounts(db);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001160 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001161 accounts.accountCache.clear();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001162 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001163 for (Entry<Long, Account> accountEntry : accountsMap.entrySet()) {
1164 final long accountId = accountEntry.getKey();
1165 final Account account = accountEntry.getValue();
1166 if (obsoleteAuthType.contains(account.type)) {
1167 Slog.w(TAG, "deleting account " + account.name + " because type "
1168 + account.type + "'s registered authenticator no longer exist.");
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001169 db.beginTransaction();
1170 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001171 AccountsDbUtils.deleteAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001172 // Also delete from CE table if user is unlocked; if user is currently
1173 // locked the account will be removed later by syncDeCeAccountsLocked
1174 if (userUnlocked) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001175 AccountsDbUtils.deleteCeAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001176 }
1177 db.setTransactionSuccessful();
1178 } finally {
1179 db.endTransaction();
1180 }
Fred Quintana56285a62010-12-02 14:20:51 -08001181 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001182
1183 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
1184 accountId, accounts);
1185
Amith Yamasani04e0d262012-02-14 11:50:53 -08001186 accounts.userDataCache.remove(account);
1187 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001188 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08001189 } else {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001190 ArrayList<String> accountNames = accountNamesByType.get(account.type);
Fred Quintana56285a62010-12-02 14:20:51 -08001191 if (accountNames == null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001192 accountNames = new ArrayList<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001193 accountNamesByType.put(account.type, accountNames);
Fred Quintana56285a62010-12-02 14:20:51 -08001194 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001195 accountNames.add(account.name);
Fred Quintana56285a62010-12-02 14:20:51 -08001196 }
1197 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001198 for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -08001199 final String accountType = cur.getKey();
1200 final ArrayList<String> accountNames = cur.getValue();
1201 final Account[] accountsForType = new Account[accountNames.size()];
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001202 for (int i = 0; i < accountsForType.length; i++) {
Svet Ganovf6d424f12016-09-20 20:18:53 -07001203 accountsForType[i] = new Account(accountNames.get(i), accountType,
1204 new AccountAccessTracker());
Fred Quintana56285a62010-12-02 14:20:51 -08001205 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001206 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -08001207 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001208 } finally {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001209 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001210 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001211 }
Fred Quintanaafa92b82009-12-01 16:27:03 -08001212 }
1213 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001214 }
1215
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001216 private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
1217 // Get the UIDs of all apps that might have data on the device. We want
1218 // to preserve user data if the app might otherwise be storing data.
1219 List<PackageInfo> pkgsWithData =
1220 mPackageManager.getInstalledPackagesAsUser(
1221 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
1222 SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
1223 for (PackageInfo pkgInfo : pkgsWithData) {
1224 if (pkgInfo.applicationInfo != null
1225 && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
1226 knownUids.put(pkgInfo.applicationInfo.uid, true);
1227 }
1228 }
1229 return knownUids;
1230 }
1231
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001232 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1233 Context context,
1234 int userId) {
1235 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001236 return getAuthenticatorTypeAndUIDForUser(authCache, userId);
1237 }
1238
1239 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1240 IAccountAuthenticatorCache authCache,
1241 int userId) {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001242 HashMap<String, Integer> knownAuth = new HashMap<>();
1243 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
1244 .getAllServices(userId)) {
1245 knownAuth.put(service.type.type, service.uid);
1246 }
1247 return knownAuth;
1248 }
1249
Amith Yamasani04e0d262012-02-14 11:50:53 -08001250 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001251 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001252 }
1253
1254 protected UserAccounts getUserAccounts(int userId) {
1255 synchronized (mUsers) {
1256 UserAccounts accounts = mUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001257 boolean validateAccounts = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001258 if (accounts == null) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001259 File preNDbFile = new File(getPreNDatabaseName(userId));
1260 File deDbFile = new File(getDeDatabaseName(userId));
1261 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001262 initializeDebugDbSizeAndCompileSqlStatementForLogging(
1263 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001264 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001265 purgeOldGrants(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001266 validateAccounts = true;
1267 }
1268 // open CE database if necessary
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001269 if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001270 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
1271 synchronized (accounts.cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001272 File preNDatabaseFile = new File(getPreNDatabaseName(userId));
1273 File ceDatabaseFile = new File(getCeDatabaseName(userId));
1274 CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
1275 accounts.openHelper.attachCeDatabase(ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001276 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001277 syncDeCeAccountsLocked(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001278 }
1279 if (validateAccounts) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001280 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001281 }
1282 return accounts;
1283 }
1284 }
1285
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001286 private void syncDeCeAccountsLocked(UserAccounts accounts) {
1287 Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
1288 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001289 List<Account> accountsToRemove = AccountsDbUtils.findCeAccountsNotInDe(db);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001290 if (!accountsToRemove.isEmpty()) {
1291 Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
1292 + accounts.userId + " was locked. Removing accounts from CE tables");
1293 logRecord(accounts, DebugDbHelper.ACTION_SYNC_DE_CE_ACCOUNTS, TABLE_ACCOUNTS);
1294
1295 for (Account account : accountsToRemove) {
1296 removeAccountInternal(accounts, account, Process.myUid());
1297 }
1298 }
1299 }
1300
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001301 private void purgeOldGrantsAll() {
1302 synchronized (mUsers) {
1303 for (int i = 0; i < mUsers.size(); i++) {
1304 purgeOldGrants(mUsers.valueAt(i));
1305 }
1306 }
1307 }
1308
1309 private void purgeOldGrants(UserAccounts accounts) {
1310 synchronized (accounts.cacheLock) {
1311 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001312 List<Integer> uids = AccountsDbUtils.findAllUidGrants(db);
1313 for (int uid : uids) {
1314 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
1315 if (packageExists) {
1316 continue;
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001317 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001318 Log.d(TAG, "deleting grants for UID " + uid
1319 + " because its package is no longer installed");
1320 AccountsDbUtils.deleteGrantsByUid(db, uid);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001321 }
1322 }
1323 }
1324
Amith Yamasani13593602012-03-22 16:16:17 -07001325 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -07001326 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -07001327 if (userId < 1) return;
1328
1329 UserAccounts accounts;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001330 boolean userUnlocked;
Amith Yamasani13593602012-03-22 16:16:17 -07001331 synchronized (mUsers) {
1332 accounts = mUsers.get(userId);
1333 mUsers.remove(userId);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001334 userUnlocked = mLocalUnlockedUsers.get(userId);
1335 mLocalUnlockedUsers.delete(userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001336 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001337 if (accounts != null) {
1338 synchronized (accounts.cacheLock) {
1339 accounts.openHelper.close();
1340 }
Amith Yamasani13593602012-03-22 16:16:17 -07001341 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001342 Log.i(TAG, "Removing database files for user " + userId);
1343 File dbFile = new File(getDeDatabaseName(userId));
Amith Yamasani13593602012-03-22 16:16:17 -07001344
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001345 deleteDbFileWarnIfFailed(dbFile);
1346 // Remove CE file if user is unlocked, or FBE is not enabled
1347 boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
1348 if (!fbeEnabled || userUnlocked) {
1349 File ceDb = new File(getCeDatabaseName(userId));
1350 if (ceDb.exists()) {
1351 deleteDbFileWarnIfFailed(ceDb);
1352 }
1353 }
1354 }
1355
1356 private static void deleteDbFileWarnIfFailed(File dbFile) {
1357 if (!SQLiteDatabase.deleteDatabase(dbFile)) {
1358 Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
Amith Yamasani13593602012-03-22 16:16:17 -07001359 }
1360 }
1361
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001362 @VisibleForTesting
1363 void onUserUnlocked(Intent intent) {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -06001364 onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
1365 }
1366
1367 void onUnlockUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001368 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1369 Log.v(TAG, "onUserUnlocked " + userId);
1370 }
1371 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001372 mLocalUnlockedUsers.put(userId, true);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001373 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001374 if (userId < 1) return;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001375 syncSharedAccounts(userId);
1376 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001377
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001378 private void syncSharedAccounts(int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001379 // Check if there's a shared account that needs to be created as an account
1380 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
1381 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -07001382 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001383 int parentUserId = UserManager.isSplitSystemUser()
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001384 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001385 : UserHandle.USER_SYSTEM;
1386 if (parentUserId < 0) {
1387 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
1388 return;
1389 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001390 for (Account sa : sharedAccounts) {
1391 if (ArrayUtils.contains(accounts, sa)) continue;
1392 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001393 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001394 }
1395 }
1396
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001397 @Override
1398 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001399 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -07001400 }
1401
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001402 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001403 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001404 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001405 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1406 Log.v(TAG, "getPassword: " + account
1407 + ", caller's uid " + Binder.getCallingUid()
1408 + ", pid " + Binder.getCallingPid());
1409 }
Fred Quintana382601f2010-03-25 12:25:10 -07001410 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001411 int userId = UserHandle.getCallingUserId();
1412 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001413 String msg = String.format(
1414 "uid %s cannot get secrets for accounts of type: %s",
1415 callingUid,
1416 account.type);
1417 throw new SecurityException(msg);
1418 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001419 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001420 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001421 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001422 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001423 } finally {
1424 restoreCallingIdentity(identityToken);
1425 }
1426 }
1427
Amith Yamasani04e0d262012-02-14 11:50:53 -08001428 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -07001429 if (account == null) {
1430 return null;
1431 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001432 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001433 Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
1434 return null;
1435 }
Fred Quintana31957f12009-10-21 13:43:10 -07001436
Amith Yamasani04e0d262012-02-14 11:50:53 -08001437 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001438 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001439 return AccountsDbUtils.findAccountPasswordByNameAndType(db, account.name,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001440 account.type);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001441 }
1442 }
1443
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001444 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001445 public String getPreviousName(Account account) {
1446 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1447 Log.v(TAG, "getPreviousName: " + account
1448 + ", caller's uid " + Binder.getCallingUid()
1449 + ", pid " + Binder.getCallingPid());
1450 }
1451 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001452 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001453 long identityToken = clearCallingIdentity();
1454 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001455 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001456 return readPreviousNameInternal(accounts, account);
1457 } finally {
1458 restoreCallingIdentity(identityToken);
1459 }
1460 }
1461
1462 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
1463 if (account == null) {
1464 return null;
1465 }
1466 synchronized (accounts.cacheLock) {
1467 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
1468 if (previousNameRef == null) {
1469 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001470 String previousName = AccountsDbUtils.findAccountPreviousName(db, account);
1471 previousNameRef = new AtomicReference<>(previousName);
1472 accounts.previousNameCache.put(account, previousNameRef);
1473 return previousName;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001474 } else {
1475 return previousNameRef.get();
1476 }
1477 }
1478 }
1479
1480 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001481 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001482 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001483 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001484 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
1485 account, key, callingUid, Binder.getCallingPid());
1486 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -08001487 }
Fred Quintana382601f2010-03-25 12:25:10 -07001488 if (account == null) throw new IllegalArgumentException("account is null");
1489 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001490 int userId = UserHandle.getCallingUserId();
1491 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001492 String msg = String.format(
1493 "uid %s cannot get user data for accounts of type: %s",
1494 callingUid,
1495 account.type);
1496 throw new SecurityException(msg);
1497 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001498 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07001499 Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
1500 return null;
1501 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001502 long identityToken = clearCallingIdentity();
1503 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001504 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00001505 synchronized (accounts.cacheLock) {
1506 if (!accountExistsCacheLocked(accounts, account)) {
1507 return null;
1508 }
1509 return readUserDataInternalLocked(accounts, account, key);
1510 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001511 } finally {
1512 restoreCallingIdentity(identityToken);
1513 }
1514 }
1515
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001516 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001517 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001518 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001519 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1520 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001521 + "for user id " + userId
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001522 + " caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001523 + ", pid " + Binder.getCallingPid());
1524 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001525 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001526 if (isCrossUser(callingUid, userId)) {
1527 throw new SecurityException(
1528 String.format(
1529 "User %s tying to get authenticator types for %s" ,
1530 UserHandle.getCallingUserId(),
1531 userId));
1532 }
1533
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001534 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001535 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001536 return getAuthenticatorTypesInternal(userId);
1537
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001538 } finally {
1539 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001540 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001541 }
1542
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001543 /**
1544 * Should only be called inside of a clearCallingIdentity block.
1545 */
1546 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
Fyodor Kupolov81446482016-08-24 11:27:49 -07001547 mAuthenticatorCache.updateServices(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001548 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
1549 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
1550 AuthenticatorDescription[] types =
1551 new AuthenticatorDescription[authenticatorCollection.size()];
1552 int i = 0;
1553 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
1554 : authenticatorCollection) {
1555 types[i] = authenticator.type;
1556 i++;
1557 }
1558 return types;
1559 }
1560
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001561 private boolean isCrossUser(int callingUid, int userId) {
1562 return (userId != UserHandle.getCallingUserId()
1563 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001564 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001565 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1566 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001567 }
1568
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001569 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07001570 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001571 Bundle.setDefusable(extras, true);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001572 // clears the visible list functionality for this account because this method allows
1573 // default account access to all applications for account.
1574
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001575 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001576 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001577 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001578 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001579 + ", pid " + Binder.getCallingPid());
1580 }
Fred Quintana382601f2010-03-25 12:25:10 -07001581 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001582 int userId = UserHandle.getCallingUserId();
1583 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001584 String msg = String.format(
1585 "uid %s cannot explicitly add accounts of type: %s",
1586 callingUid,
1587 account.type);
1588 throw new SecurityException(msg);
1589 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001590 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001591 /*
1592 * Child users are not allowed to add accounts. Only the accounts that are
1593 * shared by the parent profile can be added to child profile.
1594 *
1595 * TODO: Only allow accounts that were shared to be added by
1596 * a limited user.
1597 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001598
Fred Quintana60307342009-03-24 22:48:12 -07001599 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001600 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001601 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001602 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001603 return addAccountInternal(accounts, account, password, extras, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -07001604 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001605 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001606 }
1607 }
1608
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001609 @Override
1610 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001611 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001612 int callingUid = Binder.getCallingUid();
1613 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1614 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001615 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001616 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001617 final UserAccounts fromAccounts = getUserAccounts(userFrom);
1618 final UserAccounts toAccounts = getUserAccounts(userTo);
1619 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001620 if (response != null) {
1621 Bundle result = new Bundle();
1622 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1623 try {
1624 response.onResult(result);
1625 } catch (RemoteException e) {
1626 Slog.w(TAG, "Failed to report error back to the client." + e);
1627 }
1628 }
1629 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -08001630 }
1631
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001632 Slog.d(TAG, "Copying account " + account.name
1633 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001634 long identityToken = clearCallingIdentity();
1635 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001636 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001637 false /* stripAuthTokenFromResult */, account.name,
1638 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001639 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001640 protected String toDebugString(long now) {
1641 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1642 + ", " + account.type;
1643 }
1644
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001645 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001646 public void run() throws RemoteException {
1647 mAuthenticator.getAccountCredentialsForCloning(this, account);
1648 }
1649
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001650 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001651 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001652 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001653 if (result != null
1654 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1655 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001656 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001657 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001658 super.onResult(result);
1659 }
1660 }
1661 }.bind();
1662 } finally {
1663 restoreCallingIdentity(identityToken);
1664 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001665 }
1666
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001667 @Override
1668 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001669 final int callingUid = Binder.getCallingUid();
1670 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1671 String msg = String.format(
1672 "accountAuthenticated( account: %s, callerUid: %s)",
1673 account,
1674 callingUid);
1675 Log.v(TAG, msg);
1676 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001677 if (account == null) {
1678 throw new IllegalArgumentException("account is null");
1679 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001680 int userId = UserHandle.getCallingUserId();
1681 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001682 String msg = String.format(
1683 "uid %s cannot notify authentication for accounts of type: %s",
1684 callingUid,
1685 account.type);
1686 throw new SecurityException(msg);
1687 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001688
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001689 if (!canUserModifyAccounts(userId, callingUid) ||
1690 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001691 return false;
1692 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001693
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001694 long identityToken = clearCallingIdentity();
1695 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001696 UserAccounts accounts = getUserAccounts(userId);
1697 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001698 } finally {
1699 restoreCallingIdentity(identityToken);
1700 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07001701 }
1702
1703 private boolean updateLastAuthenticatedTime(Account account) {
1704 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001705 synchronized (accounts.cacheLock) {
1706 final ContentValues values = new ContentValues();
1707 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
1708 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1709 int i = db.update(
1710 TABLE_ACCOUNTS,
1711 values,
1712 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1713 new String[] {
1714 account.name, account.type
1715 });
1716 if (i > 0) {
1717 return true;
1718 }
1719 }
1720 return false;
1721 }
1722
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001723 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001724 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1725 final int parentUserId){
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001726 Bundle.setDefusable(accountCredentials, true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001727 long id = clearCallingIdentity();
1728 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001729 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001730 false /* stripAuthTokenFromResult */, account.name,
1731 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001732 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001733 protected String toDebugString(long now) {
1734 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1735 + ", " + account.type;
1736 }
1737
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001738 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001739 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001740 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001741 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001742 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -07001743 for (Account acc : getAccounts(parentUserId,
1744 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001745 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001746 mAuthenticator.addAccountFromCredentials(
1747 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001748 break;
1749 }
1750 }
1751 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001752 }
1753
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001754 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001755 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001756 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001757 // TODO: Anything to do if if succedded?
1758 // TODO: If it failed: Show error notification? Should we remove the shadow
1759 // account to avoid retries?
1760 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001761 }
1762
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001763 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001764 public void onError(int errorCode, String errorMessage) {
1765 super.onError(errorCode, errorMessage);
1766 // TODO: Show error notification to user
1767 // TODO: Should we remove the shadow account so that it doesn't keep trying?
1768 }
1769
1770 }.bind();
1771 } finally {
1772 restoreCallingIdentity(id);
1773 }
1774 }
1775
Amith Yamasani04e0d262012-02-14 11:50:53 -08001776 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001777 Bundle extras, int callingUid) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001778 Bundle.setDefusable(extras, true);
Fred Quintana743dfad2010-07-15 10:59:25 -07001779 if (account == null) {
1780 return false;
1781 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001782 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001783 Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
1784 + " is locked. callingUid=" + callingUid);
1785 return false;
1786 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001787 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001788 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001789 db.beginTransaction();
1790 try {
1791 long numMatches = DatabaseUtils.longForQuery(db,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001792 "select count(*) from " + CE_TABLE_ACCOUNTS
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001793 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1794 new String[]{account.name, account.type});
1795 if (numMatches > 0) {
1796 Log.w(TAG, "insertAccountIntoDatabase: " + account
1797 + ", skipping since the account already exists");
1798 return false;
1799 }
1800 ContentValues values = new ContentValues();
1801 values.put(ACCOUNTS_NAME, account.name);
1802 values.put(ACCOUNTS_TYPE, account.type);
1803 values.put(ACCOUNTS_PASSWORD, password);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001804 long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001805 if (accountId < 0) {
1806 Log.w(TAG, "insertAccountIntoDatabase: " + account
1807 + ", skipping the DB insert failed");
1808 return false;
1809 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001810 // Insert into DE table
1811 values = new ContentValues();
1812 values.put(ACCOUNTS_ID, accountId);
1813 values.put(ACCOUNTS_NAME, account.name);
1814 values.put(ACCOUNTS_TYPE, account.type);
1815 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS,
1816 System.currentTimeMillis());
1817 if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) {
1818 Log.w(TAG, "insertAccountIntoDatabase: " + account
1819 + ", skipping the DB insert failed");
1820 return false;
1821 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001822 if (extras != null) {
1823 for (String key : extras.keySet()) {
1824 final String value = extras.getString(key);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001825 if (AccountsDbUtils.insertExtra(db, accountId, key, value) < 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001826 Log.w(TAG, "insertAccountIntoDatabase: " + account
1827 + ", skipping since insertExtra failed for key " + key);
1828 return false;
1829 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001830 }
1831 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001832 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001833
1834 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
1835 accounts, callingUid);
1836
Amith Yamasani04e0d262012-02-14 11:50:53 -08001837 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001838 } finally {
1839 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001840 }
Amith Yamasani5be347b2013-03-31 17:44:31 -07001841 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001842 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1843 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001844 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001845
1846 // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1847 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001848 return true;
1849 }
1850
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001851 private boolean isLocalUnlockedUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001852 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001853 return mLocalUnlockedUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001854 }
1855 }
1856
Amith Yamasani5be347b2013-03-31 17:44:31 -07001857 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001858 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -07001859 * running, then clone the account too.
1860 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001861 *
Amith Yamasani5be347b2013-03-31 17:44:31 -07001862 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001863 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -07001864 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -07001865 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001866 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001867 addSharedAccountAsUser(account, user.id);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001868 if (isLocalUnlockedUser(user.id)) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07001869 mHandler.sendMessage(mHandler.obtainMessage(
Fyodor Kupolov041232a2016-02-22 15:01:45 -08001870 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -07001871 }
1872 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001873 }
1874 }
1875
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001876 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001877 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001878 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001879 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001880 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1881 Log.v(TAG, "hasFeatures: " + account
1882 + ", response " + response
1883 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001884 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001885 + ", pid " + Binder.getCallingPid());
1886 }
Fred Quintana382601f2010-03-25 12:25:10 -07001887 if (response == null) throw new IllegalArgumentException("response is null");
1888 if (account == null) throw new IllegalArgumentException("account is null");
1889 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001890 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001891 checkReadAccountsPermitted(callingUid, account.type, userId,
1892 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001893
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001894 long identityToken = clearCallingIdentity();
1895 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001896 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001897 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001898 } finally {
1899 restoreCallingIdentity(identityToken);
1900 }
1901 }
1902
1903 private class TestFeaturesSession extends Session {
1904 private final String[] mFeatures;
1905 private final Account mAccount;
1906
Amith Yamasani04e0d262012-02-14 11:50:53 -08001907 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001908 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001909 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001910 true /* stripAuthTokenFromResult */, account.name,
1911 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001912 mFeatures = features;
1913 mAccount = account;
1914 }
1915
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001916 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001917 public void run() throws RemoteException {
1918 try {
1919 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1920 } catch (RemoteException e) {
1921 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1922 }
1923 }
1924
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001925 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001926 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001927 Bundle.setDefusable(result, true);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001928 IAccountManagerResponse response = getResponseAndClose();
1929 if (response != null) {
1930 try {
1931 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001932 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001933 return;
1934 }
Fred Quintana56285a62010-12-02 14:20:51 -08001935 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1936 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1937 + response);
1938 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001939 final Bundle newResult = new Bundle();
1940 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1941 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1942 response.onResult(newResult);
1943 } catch (RemoteException e) {
1944 // if the caller is dead then there is no one to care about remote exceptions
1945 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1946 Log.v(TAG, "failure while notifying response", e);
1947 }
1948 }
1949 }
1950 }
1951
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001952 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001953 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001954 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001955 + ", " + mAccount
1956 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1957 }
1958 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001959
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001960 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001961 public void renameAccount(
1962 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001963 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001964 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1965 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001966 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001967 + ", pid " + Binder.getCallingPid());
1968 }
1969 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001970 int userId = UserHandle.getCallingUserId();
1971 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001972 String msg = String.format(
1973 "uid %s cannot rename accounts of type: %s",
1974 callingUid,
1975 accountToRename.type);
1976 throw new SecurityException(msg);
1977 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001978 long identityToken = clearCallingIdentity();
1979 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001980 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001981 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001982 Bundle result = new Bundle();
1983 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1984 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
Svet Ganovf6d424f12016-09-20 20:18:53 -07001985 result.putBinder(AccountManager.KEY_ACCOUNT_ACCESS_TRACKER,
1986 resultingAccount.getAccessTracker().asBinder());
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001987 try {
1988 response.onResult(result);
1989 } catch (RemoteException e) {
1990 Log.w(TAG, e.getMessage());
1991 }
1992 } finally {
1993 restoreCallingIdentity(identityToken);
1994 }
1995 }
1996
1997 private Account renameAccountInternal(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001998 UserAccounts accounts, Account accountToRename, String newName) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001999 Account resultAccount = null;
2000 /*
2001 * Cancel existing notifications. Let authenticators
2002 * re-post notifications as required. But we don't know if
2003 * the authenticators have bound their notifications to
2004 * now stale account name data.
2005 *
2006 * With a rename api, we might not need to do this anymore but it
2007 * shouldn't hurt.
2008 */
2009 cancelNotification(
2010 getSigninRequiredNotificationId(accounts, accountToRename),
2011 new UserHandle(accounts.userId));
2012 synchronized(accounts.credentialsPermissionNotificationIds) {
2013 for (Pair<Pair<Account, String>, Integer> pair:
2014 accounts.credentialsPermissionNotificationIds.keySet()) {
2015 if (accountToRename.equals(pair.first.first)) {
2016 int id = accounts.credentialsPermissionNotificationIds.get(pair);
2017 cancelNotification(id, new UserHandle(accounts.userId));
2018 }
2019 }
2020 }
2021 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002022 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002023 db.beginTransaction();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002024 Account renamedAccount = new Account(newName, accountToRename.type);
2025 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002026 final long accountId = AccountsDbUtils.findAccountId(db, accountToRename);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002027 if (accountId >= 0) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002028 final ContentValues values = new ContentValues();
2029 values.put(ACCOUNTS_NAME, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002030 final String[] argsAccountId = { String.valueOf(accountId) };
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002031 db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
2032 // Update NAME/PREVIOUS_NAME in DE accounts table
2033 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002034 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
2035 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002036 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
2037 accounts);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002038 }
2039 } finally {
2040 db.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002041 }
2042 /*
2043 * Database transaction was successful. Clean up cached
2044 * data associated with the account in the user profile.
Svetoslav Ganov57f62592016-09-16 17:29:05 -07002045 * The account is now being tracked for remote access.
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002046 */
Svetoslav Ganov57f62592016-09-16 17:29:05 -07002047 renamedAccount = insertAccountIntoCacheLocked(accounts, renamedAccount);
2048
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002049 /*
2050 * Extract the data and token caches before removing the
2051 * old account to preserve the user data associated with
2052 * the account.
2053 */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002054 Map<String, String> tmpData = accounts.userDataCache.get(accountToRename);
2055 Map<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002056 removeAccountFromCacheLocked(accounts, accountToRename);
2057 /*
2058 * Update the cached data associated with the renamed
2059 * account.
2060 */
2061 accounts.userDataCache.put(renamedAccount, tmpData);
2062 accounts.authTokenCache.put(renamedAccount, tmpTokens);
2063 accounts.previousNameCache.put(
2064 renamedAccount,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002065 new AtomicReference<>(accountToRename.name));
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002066 resultAccount = renamedAccount;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002067
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002068 int parentUserId = accounts.userId;
2069 if (canHaveProfile(parentUserId)) {
2070 /*
2071 * Owner or system user account was renamed, rename the account for
2072 * those users with which the account was shared.
2073 */
2074 List<UserInfo> users = getUserManager().getUsers(true);
2075 for (UserInfo user : users) {
2076 if (user.isRestricted()
2077 && (user.restrictedProfileParentId == parentUserId)) {
2078 renameSharedAccountAsUser(accountToRename, newName, user.id);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002079 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002080 }
2081 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002082 sendAccountsChangedBroadcast(accounts.userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002083 }
2084 return resultAccount;
2085 }
2086
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002087 private boolean canHaveProfile(final int parentUserId) {
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002088 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002089 return userInfo != null && userInfo.canHaveProfile();
2090 }
2091
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002092 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002093 public void removeAccount(IAccountManagerResponse response, Account account,
2094 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002095 removeAccountAsUser(
2096 response,
2097 account,
2098 expectActivityLaunch,
2099 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002100 }
2101
2102 @Override
2103 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002104 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002105 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002106 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2107 Log.v(TAG, "removeAccount: " + account
2108 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002109 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002110 + ", pid " + Binder.getCallingPid()
2111 + ", for user id " + userId);
2112 }
2113 if (response == null) throw new IllegalArgumentException("response is null");
2114 if (account == null) throw new IllegalArgumentException("account is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002115 // Only allow the system process to modify accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002116 if (isCrossUser(callingUid, userId)) {
2117 throw new SecurityException(
2118 String.format(
2119 "User %s tying remove account for %s" ,
2120 UserHandle.getCallingUserId(),
2121 userId));
2122 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002123 /*
2124 * Only the system or authenticator should be allowed to remove accounts for that
2125 * authenticator. This will let users remove accounts (via Settings in the system) but not
2126 * arbitrary applications (like competing authenticators).
2127 */
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002128 UserHandle user = UserHandle.of(userId);
Ian Pedowitz358e51f2016-03-15 17:08:27 +00002129 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
2130 && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002131 String msg = String.format(
2132 "uid %s cannot remove accounts of type: %s",
2133 callingUid,
2134 account.type);
2135 throw new SecurityException(msg);
2136 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002137 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002138 try {
2139 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2140 "User cannot modify accounts");
2141 } catch (RemoteException re) {
2142 }
2143 return;
2144 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002145 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002146 try {
2147 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2148 "User cannot modify accounts of this type (policy).");
2149 } catch (RemoteException re) {
2150 }
2151 return;
2152 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002153 long identityToken = clearCallingIdentity();
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002154 UserAccounts accounts = getUserAccounts(userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002155 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002156 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002157 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08002158 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002159 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002160 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002161 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002162 }
2163 }
2164 }
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002165 SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002166 final long accountId = AccountsDbUtils.findAccountId(db, account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002167 logRecord(
2168 db,
2169 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
2170 TABLE_ACCOUNTS,
2171 accountId,
2172 accounts,
2173 callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002174 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002175 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
2176 } finally {
2177 restoreCallingIdentity(identityToken);
2178 }
2179 }
2180
2181 @Override
2182 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002183 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002184 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2185 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002186 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002187 + ", pid " + Binder.getCallingPid());
2188 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002189 int userId = Binder.getCallingUserHandle().getIdentifier();
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002190 if (account == null) {
2191 /*
2192 * Null accounts should result in returning false, as per
2193 * AccountManage.addAccountExplicitly(...) java doc.
2194 */
2195 Log.e(TAG, "account is null");
2196 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002197 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002198 String msg = String.format(
2199 "uid %s cannot explicitly add accounts of type: %s",
2200 callingUid,
2201 account.type);
2202 throw new SecurityException(msg);
2203 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07002204 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002205 UserAccounts accounts = getUserAccountsForCaller();
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002206 SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002207 final long accountId = AccountsDbUtils.findAccountId(db, account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002208 logRecord(
2209 db,
2210 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
2211 TABLE_ACCOUNTS,
2212 accountId,
2213 accounts,
2214 callingUid);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002215 long identityToken = clearCallingIdentity();
2216 try {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002217 return removeAccountInternal(accounts, account, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002218 } finally {
2219 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07002220 }
Fred Quintana60307342009-03-24 22:48:12 -07002221 }
2222
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002223 private class RemoveAccountSession extends Session {
2224 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002225 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002226 Account account, boolean expectActivityLaunch) {
2227 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002228 true /* stripAuthTokenFromResult */, account.name,
2229 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002230 mAccount = account;
2231 }
2232
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002233 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002234 protected String toDebugString(long now) {
2235 return super.toDebugString(now) + ", removeAccount"
2236 + ", account " + mAccount;
2237 }
2238
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002239 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002240 public void run() throws RemoteException {
2241 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
2242 }
2243
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002244 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002245 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002246 Bundle.setDefusable(result, true);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002247 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
2248 && !result.containsKey(AccountManager.KEY_INTENT)) {
2249 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002250 if (removalAllowed) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002251 removeAccountInternal(mAccounts, mAccount, getCallingUid());
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002252 }
2253 IAccountManagerResponse response = getResponseAndClose();
2254 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08002255 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2256 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2257 + response);
2258 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002259 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002260 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002261 try {
2262 response.onResult(result2);
2263 } catch (RemoteException e) {
2264 // ignore
2265 }
2266 }
2267 }
2268 super.onResult(result);
2269 }
2270 }
2271
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07002272 @VisibleForTesting
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002273 protected void removeAccountInternal(Account account) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002274 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
Amith Yamasani04e0d262012-02-14 11:50:53 -08002275 }
2276
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002277 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002278 boolean isChanged = false;
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002279 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002280 if (!userUnlocked) {
2281 Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
2282 + " is still locked. CE data will be removed later");
2283 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002284 synchronized (accounts.cacheLock) {
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002285 final SQLiteDatabase db = userUnlocked
2286 ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
2287 : accounts.openHelper.getWritableDatabase();
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002288 db.beginTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002289 // Set to a dummy value, this will only be used if the database
2290 // transaction succeeds.
2291 long accountId = -1;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002292 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002293 accountId = AccountsDbUtils.findAccountId(db, account);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002294 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002295 AccountsDbUtils.deleteAccount(db, accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002296 if (userUnlocked) {
2297 // Delete from CE table
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002298 AccountsDbUtils.deleteCeAccount(db, accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002299 }
2300 db.setTransactionSuccessful();
2301 isChanged = true;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002302 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002303 } finally {
2304 db.endTransaction();
2305 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002306 if (isChanged) {
2307 removeAccountFromCacheLocked(accounts, account);
2308 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
2309 sendAccountsChangedBroadcast(accounts.userId);
2310 String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE
2311 : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE;
2312 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts);
2313 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002314 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002315 long id = Binder.clearCallingIdentity();
2316 try {
2317 int parentUserId = accounts.userId;
2318 if (canHaveProfile(parentUserId)) {
2319 // Remove from any restricted profiles that are sharing this account.
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002320 List<UserInfo> users = getUserManager().getUsers(true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002321 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002322 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002323 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002324 }
2325 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08002326 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002327 } finally {
2328 Binder.restoreCallingIdentity(id);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002329 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002330
2331 if (isChanged) {
2332 synchronized (accounts.credentialsPermissionNotificationIds) {
2333 for (Pair<Pair<Account, String>, Integer> key
2334 : accounts.credentialsPermissionNotificationIds.keySet()) {
2335 if (account.equals(key.first.first)
Svet Ganovf6d424f12016-09-20 20:18:53 -07002336 && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002337 final int uid = (Integer) key.second;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07002338 mHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002339 account, uid, false));
2340 }
2341 }
2342 }
2343 }
2344
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002345 return isChanged;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002346 }
2347
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002348 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002349 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002350 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002351 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2352 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07002353 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08002354 + ", pid " + Binder.getCallingPid());
2355 }
Fred Quintana382601f2010-03-25 12:25:10 -07002356 if (accountType == null) throw new IllegalArgumentException("accountType is null");
2357 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002358 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002359 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002360 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002361 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002362 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002363 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002364 db.beginTransaction();
2365 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002366 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002367 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002368 db.setTransactionSuccessful();
2369 } finally {
2370 db.endTransaction();
2371 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002372 }
Fred Quintana60307342009-03-24 22:48:12 -07002373 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002374 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002375 }
2376 }
2377
Carlos Valdivia91979be2015-05-22 14:11:35 -07002378 private void invalidateCustomTokenLocked(
2379 UserAccounts accounts,
2380 String accountType,
2381 String authToken) {
2382 if (authToken == null || accountType == null) {
2383 return;
2384 }
2385 // Also wipe out cached token in memory.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002386 accounts.accountTokenCaches.remove(accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002387 }
2388
Amith Yamasani04e0d262012-02-14 11:50:53 -08002389 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
2390 String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002391 if (authToken == null || accountType == null) {
2392 return;
2393 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002394 Cursor cursor = AccountsDbUtils.findAuthtokenForAllAccounts(db, accountType, authToken);
Fred Quintana33269202009-04-20 16:05:10 -07002395 try {
2396 while (cursor.moveToNext()) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002397 String authTokenId = cursor.getString(0);
Fred Quintana33269202009-04-20 16:05:10 -07002398 String accountName = cursor.getString(1);
2399 String authTokenType = cursor.getString(2);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002400 AccountsDbUtils.deleteAuthToken(db, authTokenId);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002401 writeAuthTokenIntoCacheLocked(
2402 accounts,
2403 db,
2404 new Account(accountName, accountType),
2405 authTokenType,
2406 null);
Fred Quintana60307342009-03-24 22:48:12 -07002407 }
Fred Quintana33269202009-04-20 16:05:10 -07002408 } finally {
2409 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07002410 }
2411 }
2412
Carlos Valdivia91979be2015-05-22 14:11:35 -07002413 private void saveCachedToken(
2414 UserAccounts accounts,
2415 Account account,
2416 String callerPkg,
2417 byte[] callerSigDigest,
2418 String tokenType,
2419 String token,
2420 long expiryMillis) {
2421
2422 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
2423 return;
2424 }
2425 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002426 UserHandle.of(accounts.userId));
Carlos Valdivia91979be2015-05-22 14:11:35 -07002427 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002428 accounts.accountTokenCaches.put(
2429 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002430 }
2431 }
2432
Amith Yamasani04e0d262012-02-14 11:50:53 -08002433 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
2434 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07002435 if (account == null || type == null) {
2436 return false;
2437 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002438 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002439 UserHandle.of(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002440 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002441 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002442 db.beginTransaction();
2443 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002444 long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002445 if (accountId < 0) {
2446 return false;
2447 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002448 AccountsDbUtils.deleteAuthtokensByAccountIdAndType(db, accountId, type);
2449 if (AccountsDbUtils.insertAuthToken(db, accountId, type, authToken) >= 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002450 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002451 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002452 return true;
2453 }
Fred Quintana33269202009-04-20 16:05:10 -07002454 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002455 } finally {
2456 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07002457 }
Fred Quintana60307342009-03-24 22:48:12 -07002458 }
2459 }
2460
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002461 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002462 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002463 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002464 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2465 Log.v(TAG, "peekAuthToken: " + account
2466 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002467 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002468 + ", pid " + Binder.getCallingPid());
2469 }
Fred Quintana382601f2010-03-25 12:25:10 -07002470 if (account == null) throw new IllegalArgumentException("account is null");
2471 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002472 int userId = UserHandle.getCallingUserId();
2473 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002474 String msg = String.format(
2475 "uid %s cannot peek the authtokens associated with accounts of type: %s",
2476 callingUid,
2477 account.type);
2478 throw new SecurityException(msg);
2479 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002480 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07002481 Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
2482 + callingUid);
2483 return null;
2484 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002485 long identityToken = clearCallingIdentity();
2486 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002487 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002488 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002489 } finally {
2490 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002491 }
Fred Quintana60307342009-03-24 22:48:12 -07002492 }
2493
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002494 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002495 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002496 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002497 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2498 Log.v(TAG, "setAuthToken: " + account
2499 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002500 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002501 + ", pid " + Binder.getCallingPid());
2502 }
Fred Quintana382601f2010-03-25 12:25:10 -07002503 if (account == null) throw new IllegalArgumentException("account is null");
2504 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002505 int userId = UserHandle.getCallingUserId();
2506 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002507 String msg = String.format(
2508 "uid %s cannot set auth tokens associated with accounts of type: %s",
2509 callingUid,
2510 account.type);
2511 throw new SecurityException(msg);
2512 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002513 long identityToken = clearCallingIdentity();
2514 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002515 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002516 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002517 } finally {
2518 restoreCallingIdentity(identityToken);
2519 }
Fred Quintana60307342009-03-24 22:48:12 -07002520 }
2521
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002522 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002523 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002524 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002525 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2526 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002527 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002528 + ", pid " + Binder.getCallingPid());
2529 }
Fred Quintana382601f2010-03-25 12:25:10 -07002530 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002531 int userId = UserHandle.getCallingUserId();
2532 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002533 String msg = String.format(
2534 "uid %s cannot set secrets for accounts of type: %s",
2535 callingUid,
2536 account.type);
2537 throw new SecurityException(msg);
2538 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002539 long identityToken = clearCallingIdentity();
2540 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002541 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002542 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002543 } finally {
2544 restoreCallingIdentity(identityToken);
2545 }
Fred Quintana60307342009-03-24 22:48:12 -07002546 }
2547
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002548 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
2549 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07002550 if (account == null) {
2551 return;
2552 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002553 boolean isChanged = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002554 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002555 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002556 db.beginTransaction();
2557 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002558 final long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002559 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002560 AccountsDbUtils.updateAccountPassword(db, accountId, password);
2561 AccountsDbUtils.deleteAuthTokensByAccountId(db, accountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002562 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002563 accounts.accountTokenCaches.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002564 db.setTransactionSuccessful();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002565 // If there is an account whose password will be updated and the database
2566 // transactions succeed, then we say that a change has occured. Even if the
2567 // new password is the same as the old and there were no authtokens to delete.
2568 isChanged = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002569 String action = (password == null || password.length() == 0) ?
2570 DebugDbHelper.ACTION_CLEAR_PASSWORD
2571 : DebugDbHelper.ACTION_SET_PASSWORD;
2572 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08002573 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002574 } finally {
2575 db.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002576 if (isChanged) {
2577 // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
2578 sendAccountsChangedBroadcast(accounts.userId);
2579 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002580 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002581 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07002582 }
2583
Amith Yamasani04e0d262012-02-14 11:50:53 -08002584 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08002585 Log.i(TAG, "the accounts changed, sending broadcast of "
2586 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07002587 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07002588 }
2589
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002590 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002591 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002592 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002593 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2594 Log.v(TAG, "clearPassword: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002595 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002596 + ", pid " + Binder.getCallingPid());
2597 }
Fred Quintana382601f2010-03-25 12:25:10 -07002598 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002599 int userId = UserHandle.getCallingUserId();
2600 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002601 String msg = String.format(
2602 "uid %s cannot clear passwords for accounts of type: %s",
2603 callingUid,
2604 account.type);
2605 throw new SecurityException(msg);
2606 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002607 long identityToken = clearCallingIdentity();
2608 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002609 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002610 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002611 } finally {
2612 restoreCallingIdentity(identityToken);
2613 }
Fred Quintana60307342009-03-24 22:48:12 -07002614 }
2615
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002616 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002617 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002618 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002619 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2620 Log.v(TAG, "setUserData: " + account
2621 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002622 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002623 + ", pid " + Binder.getCallingPid());
2624 }
Fred Quintana382601f2010-03-25 12:25:10 -07002625 if (key == null) throw new IllegalArgumentException("key is null");
2626 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002627 int userId = UserHandle.getCallingUserId();
2628 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002629 String msg = String.format(
2630 "uid %s cannot set user data for accounts of type: %s",
2631 callingUid,
2632 account.type);
2633 throw new SecurityException(msg);
2634 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002635 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002636 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002637 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002638 synchronized (accounts.cacheLock) {
2639 if (!accountExistsCacheLocked(accounts, account)) {
2640 return;
2641 }
2642 setUserdataInternalLocked(accounts, account, key, value);
2643 }
Fred Quintana60307342009-03-24 22:48:12 -07002644 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002645 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002646 }
2647 }
2648
Simranjit Kohli858511c2016-03-10 18:36:11 +00002649 private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
2650 if (accounts.accountCache.containsKey(account.type)) {
2651 for (Account acc : accounts.accountCache.get(account.type)) {
2652 if (acc.name.equals(account.name)) {
2653 return true;
2654 }
2655 }
2656 }
2657 return false;
2658 }
2659
2660 private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
Amith Yamasani04e0d262012-02-14 11:50:53 -08002661 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07002662 if (account == null || key == null) {
2663 return;
2664 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00002665 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
2666 db.beginTransaction();
2667 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002668 long accountId = AccountsDbUtils.findAccountId(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002669 if (accountId < 0) {
2670 return;
2671 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002672 long extrasId = AccountsDbUtils.findExtrasIdByAccountId(db, accountId, key);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002673 if (extrasId < 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002674 extrasId = AccountsDbUtils.insertExtra(db, accountId, key, value);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002675 if (extrasId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002676 return;
2677 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002678 } else if (!AccountsDbUtils.updateExtra(db, extrasId, value)) {
2679 return;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002680 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00002681 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
2682 db.setTransactionSuccessful();
2683 } finally {
2684 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002685 }
2686 }
2687
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002688 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08002689 if (result == null) {
2690 Log.e(TAG, "the result is unexpectedly null", new Exception());
2691 }
2692 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2693 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2694 + response);
2695 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002696 try {
2697 response.onResult(result);
2698 } catch (RemoteException e) {
2699 // if the caller is dead then there is no one to care about remote
2700 // exceptions
2701 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2702 Log.v(TAG, "failure while notifying response", e);
2703 }
2704 }
2705 }
2706
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002707 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07002708 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
2709 final String authTokenType)
2710 throws RemoteException {
2711 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08002712 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2713
Fred Quintanad9640ec2012-05-23 12:37:00 -07002714 final int callingUid = getCallingUid();
2715 clearCallingIdentity();
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07002716 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07002717 throw new SecurityException("can only call from system");
2718 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002719 int userId = UserHandle.getUserId(callingUid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002720 long identityToken = clearCallingIdentity();
2721 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002722 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002723 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
2724 false /* stripAuthTokenFromResult */, null /* accountName */,
2725 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002726 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002727 protected String toDebugString(long now) {
2728 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07002729 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08002730 + ", authTokenType " + authTokenType;
2731 }
2732
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002733 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002734 public void run() throws RemoteException {
2735 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2736 }
2737
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002738 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002739 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002740 Bundle.setDefusable(result, true);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002741 if (result != null) {
2742 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
2743 Bundle bundle = new Bundle();
2744 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
2745 super.onResult(bundle);
2746 return;
2747 } else {
2748 super.onResult(result);
2749 }
2750 }
2751 }.bind();
2752 } finally {
2753 restoreCallingIdentity(identityToken);
2754 }
2755 }
2756
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002757 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07002758 public void getAuthToken(
2759 IAccountManagerResponse response,
2760 final Account account,
2761 final String authTokenType,
2762 final boolean notifyOnAuthFailure,
2763 final boolean expectActivityLaunch,
2764 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002765 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002766 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2767 Log.v(TAG, "getAuthToken: " + account
2768 + ", response " + response
2769 + ", authTokenType " + authTokenType
2770 + ", notifyOnAuthFailure " + notifyOnAuthFailure
2771 + ", expectActivityLaunch " + expectActivityLaunch
2772 + ", caller's uid " + Binder.getCallingUid()
2773 + ", pid " + Binder.getCallingPid());
2774 }
Fred Quintana382601f2010-03-25 12:25:10 -07002775 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002776 try {
2777 if (account == null) {
2778 Slog.w(TAG, "getAuthToken called with null account");
2779 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
2780 return;
2781 }
2782 if (authTokenType == null) {
2783 Slog.w(TAG, "getAuthToken called with null authTokenType");
2784 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
2785 return;
2786 }
2787 } catch (RemoteException e) {
2788 Slog.w(TAG, "Failed to report error back to the client." + e);
2789 return;
2790 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002791 int userId = UserHandle.getCallingUserId();
2792 long ident = Binder.clearCallingIdentity();
2793 final UserAccounts accounts;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002794 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002795 try {
2796 accounts = getUserAccounts(userId);
2797 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2798 AuthenticatorDescription.newKey(account.type), accounts.userId);
2799 } finally {
2800 Binder.restoreCallingIdentity(ident);
2801 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002802
Costin Manolachea40c6302010-12-13 14:50:45 -08002803 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07002804 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08002805
2806 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002807 final int callerUid = Binder.getCallingUid();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002808 final boolean permissionGranted =
2809 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08002810
Carlos Valdivia91979be2015-05-22 14:11:35 -07002811 // Get the calling package. We will use it for the purpose of caching.
2812 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07002813 List<String> callerOwnedPackageNames;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002814 ident = Binder.clearCallingIdentity();
Amith Yamasanie7360012015-06-03 17:39:40 -07002815 try {
2816 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
2817 } finally {
2818 Binder.restoreCallingIdentity(ident);
2819 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002820 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
2821 String msg = String.format(
2822 "Uid %s is attempting to illegally masquerade as package %s!",
2823 callerUid,
2824 callerPkg);
2825 throw new SecurityException(msg);
2826 }
2827
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002828 // let authenticator know the identity of the caller
2829 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
2830 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07002831
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002832 if (notifyOnAuthFailure) {
2833 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08002834 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002835
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002836 long identityToken = clearCallingIdentity();
2837 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07002838 // Distill the caller's package signatures into a single digest.
2839 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
2840
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002841 // if the caller has permission, do the peek. otherwise go the more expensive
2842 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08002843 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002844 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002845 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002846 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002847 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
2848 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2849 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002850 onResult(response, result);
2851 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002852 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002853 }
2854
Carlos Valdivia91979be2015-05-22 14:11:35 -07002855 if (customTokens) {
2856 /*
2857 * Look up tokens in the new cache only if the loginOptions don't have parameters
2858 * outside of those expected to be injected by the AccountManager, e.g.
2859 * ANDORID_PACKAGE_NAME.
2860 */
2861 String token = readCachedTokenInternal(
2862 accounts,
2863 account,
2864 authTokenType,
2865 callerPkg,
2866 callerPkgSigDigest);
2867 if (token != null) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002868 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2869 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2870 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002871 Bundle result = new Bundle();
2872 result.putString(AccountManager.KEY_AUTHTOKEN, token);
2873 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2874 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2875 onResult(response, result);
2876 return;
2877 }
2878 }
2879
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002880 new Session(
2881 accounts,
2882 response,
2883 account.type,
2884 expectActivityLaunch,
2885 false /* stripAuthTokenFromResult */,
2886 account.name,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002887 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002888 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002889 protected String toDebugString(long now) {
2890 if (loginOptions != null) loginOptions.keySet();
2891 return super.toDebugString(now) + ", getAuthToken"
2892 + ", " + account
2893 + ", authTokenType " + authTokenType
2894 + ", loginOptions " + loginOptions
2895 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2896 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002897
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002898 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002899 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002900 // If the caller doesn't have permission then create and return the
2901 // "grant permission" intent instead of the "getAuthToken" intent.
2902 if (!permissionGranted) {
2903 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2904 } else {
2905 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2906 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002907 }
2908
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002909 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002910 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002911 Bundle.setDefusable(result, true);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002912 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002913 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002914 Intent intent = newGrantCredentialsPermissionIntent(
2915 account,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002916 null,
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002917 callerUid,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002918 new AccountAuthenticatorResponse(this),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002919 authTokenType,
2920 true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002921 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002922 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002923 onResult(bundle);
2924 return;
2925 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002926 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002927 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002928 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2929 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002930 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002931 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002932 "the type and name should not be empty");
2933 return;
2934 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002935 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08002936 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002937 saveAuthTokenToDatabase(
2938 mAccounts,
2939 resultAccount,
2940 authTokenType,
2941 authToken);
2942 }
2943 long expiryMillis = result.getLong(
2944 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2945 if (customTokens
2946 && expiryMillis > System.currentTimeMillis()) {
2947 saveCachedToken(
2948 mAccounts,
2949 account,
2950 callerPkg,
2951 callerPkgSigDigest,
2952 authTokenType,
2953 authToken,
2954 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08002955 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002956 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002957
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002958 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08002959 if (intent != null && notifyOnAuthFailure && !customTokens) {
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002960 /*
2961 * Make sure that the supplied intent is owned by the authenticator
2962 * giving it to the system. Otherwise a malicious authenticator could
2963 * have users launching arbitrary activities by tricking users to
2964 * interact with malicious notifications.
2965 */
2966 checkKeyIntent(
2967 Binder.getCallingUid(),
2968 intent);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002969 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002970 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002971 intent, "android", accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002972 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002973 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002974 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07002975 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002976 }.bind();
2977 } finally {
2978 restoreCallingIdentity(identityToken);
2979 }
Fred Quintana60307342009-03-24 22:48:12 -07002980 }
2981
Carlos Valdivia91979be2015-05-22 14:11:35 -07002982 private byte[] calculatePackageSignatureDigest(String callerPkg) {
2983 MessageDigest digester;
2984 try {
2985 digester = MessageDigest.getInstance("SHA-256");
2986 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2987 callerPkg, PackageManager.GET_SIGNATURES);
2988 for (Signature sig : pkgInfo.signatures) {
2989 digester.update(sig.toByteArray());
2990 }
2991 } catch (NoSuchAlgorithmException x) {
2992 Log.wtf(TAG, "SHA-256 should be available", x);
2993 digester = null;
2994 } catch (NameNotFoundException e) {
2995 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2996 digester = null;
2997 }
2998 return (digester == null) ? null : digester.digest();
2999 }
3000
Dianne Hackborn41203752012-08-31 14:05:51 -07003001 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003002 String packageName, int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003003 int uid = intent.getIntExtra(
3004 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
3005 String authTokenType = intent.getStringExtra(
3006 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
Eric Fischeree452ee2009-08-31 17:58:06 -07003007 final String titleAndSubtitle =
3008 mContext.getString(R.string.permission_request_notification_with_subtitle,
3009 account.name);
3010 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07003011 String title = titleAndSubtitle;
3012 String subtitle = "";
3013 if (index > 0) {
3014 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04003015 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07003016 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07003017 UserHandle user = UserHandle.of(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01003018 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04003019 Notification n = new Notification.Builder(contextForUser)
3020 .setSmallIcon(android.R.drawable.stat_sys_warning)
3021 .setWhen(0)
3022 .setColor(contextForUser.getColor(
3023 com.android.internal.R.color.system_notification_accent_color))
3024 .setContentTitle(title)
3025 .setContentText(subtitle)
3026 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
3027 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
3028 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003029 installNotification(getCredentialPermissionNotificationId(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003030 account, authTokenType, uid), n, packageName, user.getIdentifier());
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003031 }
3032
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003033 private Intent newGrantCredentialsPermissionIntent(Account account, String packageName,
3034 int uid, AccountAuthenticatorResponse response, String authTokenType,
3035 boolean startInNewTask) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003036
3037 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Costin Manolache5f383ad92010-12-02 16:44:46 -08003038
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003039 if (startInNewTask) {
3040 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
3041 // Since it was set in Eclair+ we can't change it without breaking apps using
3042 // the intent from a non-Activity context. This is the default behavior.
3043 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3044 }
3045 intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account,
3046 authTokenType, uid) + (packageName != null ? packageName : "")));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003047 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003048 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
3049 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003050 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08003051
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003052 return intent;
3053 }
3054
3055 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
3056 int uid) {
3057 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07003058 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08003059 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003060 final Pair<Pair<Account, String>, Integer> key =
3061 new Pair<Pair<Account, String>, Integer>(
3062 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003063 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003064 if (id == null) {
3065 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08003066 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003067 }
3068 }
3069 return id;
3070 }
3071
Amith Yamasani04e0d262012-02-14 11:50:53 -08003072 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003073 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003074 synchronized (accounts.signinRequiredNotificationIds) {
3075 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003076 if (id == null) {
3077 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08003078 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003079 }
3080 }
3081 return id;
3082 }
3083
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003084 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07003085 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07003086 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003087 final boolean expectActivityLaunch, final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003088 Bundle.setDefusable(optionsIn, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003089 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3090 Log.v(TAG, "addAccount: accountType " + accountType
3091 + ", response " + response
3092 + ", authTokenType " + authTokenType
3093 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3094 + ", expectActivityLaunch " + expectActivityLaunch
3095 + ", caller's uid " + Binder.getCallingUid()
3096 + ", pid " + Binder.getCallingPid());
3097 }
Fred Quintana382601f2010-03-25 12:25:10 -07003098 if (response == null) throw new IllegalArgumentException("response is null");
3099 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003100
Amith Yamasani71e6c692013-03-24 17:39:28 -07003101 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003102 final int uid = Binder.getCallingUid();
3103 final int userId = UserHandle.getUserId(uid);
3104 if (!canUserModifyAccounts(userId, uid)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003105 try {
3106 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3107 "User is not allowed to add an account!");
3108 } catch (RemoteException re) {
3109 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003110 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003111 return;
3112 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003113 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07003114 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003115 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3116 "User cannot modify accounts of this type (policy).");
3117 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07003118 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003119 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3120 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003121 return;
3122 }
3123
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003124 final int pid = Binder.getCallingPid();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003125 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3126 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3127 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3128
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003129 int usrId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003130 long identityToken = clearCallingIdentity();
3131 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003132 UserAccounts accounts = getUserAccounts(usrId);
3133 logRecordWithUid(
3134 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003135 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003136 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003137 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003138 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003139 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07003140 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07003141 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003142 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003143
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003144 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003145 protected String toDebugString(long now) {
3146 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07003147 + ", accountType " + accountType
3148 + ", requiredFeatures "
3149 + (requiredFeatures != null
3150 ? TextUtils.join(",", requiredFeatures)
3151 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003152 }
3153 }.bind();
3154 } finally {
3155 restoreCallingIdentity(identityToken);
3156 }
Fred Quintana60307342009-03-24 22:48:12 -07003157 }
3158
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003159 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003160 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
3161 final String authTokenType, final String[] requiredFeatures,
3162 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003163 Bundle.setDefusable(optionsIn, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003164 int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003165 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3166 Log.v(TAG, "addAccount: accountType " + accountType
3167 + ", response " + response
3168 + ", authTokenType " + authTokenType
3169 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3170 + ", expectActivityLaunch " + expectActivityLaunch
3171 + ", caller's uid " + Binder.getCallingUid()
3172 + ", pid " + Binder.getCallingPid()
3173 + ", for user id " + userId);
3174 }
3175 if (response == null) throw new IllegalArgumentException("response is null");
3176 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003177 // Only allow the system process to add accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003178 if (isCrossUser(callingUid, userId)) {
3179 throw new SecurityException(
3180 String.format(
3181 "User %s trying to add account for %s" ,
3182 UserHandle.getCallingUserId(),
3183 userId));
3184 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003185
3186 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003187 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003188 try {
3189 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3190 "User is not allowed to add an account!");
3191 } catch (RemoteException re) {
3192 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003193 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003194 return;
3195 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003196 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003197 try {
3198 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3199 "User cannot modify accounts of this type (policy).");
3200 } catch (RemoteException re) {
3201 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003202 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3203 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003204 return;
3205 }
3206
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003207 final int pid = Binder.getCallingPid();
3208 final int uid = Binder.getCallingUid();
3209 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3210 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3211 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3212
3213 long identityToken = clearCallingIdentity();
3214 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003215 UserAccounts accounts = getUserAccounts(userId);
3216 logRecordWithUid(
3217 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003218 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003219 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003220 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003221 @Override
3222 public void run() throws RemoteException {
3223 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
3224 options);
3225 }
3226
3227 @Override
3228 protected String toDebugString(long now) {
3229 return super.toDebugString(now) + ", addAccount"
3230 + ", accountType " + accountType
3231 + ", requiredFeatures "
3232 + (requiredFeatures != null
3233 ? TextUtils.join(",", requiredFeatures)
3234 : null);
3235 }
3236 }.bind();
3237 } finally {
3238 restoreCallingIdentity(identityToken);
3239 }
3240 }
3241
Sandra Kwan78812282015-11-04 11:19:47 -08003242 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003243 public void startAddAccountSession(
3244 final IAccountManagerResponse response,
3245 final String accountType,
3246 final String authTokenType,
3247 final String[] requiredFeatures,
Sandra Kwan78812282015-11-04 11:19:47 -08003248 final boolean expectActivityLaunch,
3249 final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003250 Bundle.setDefusable(optionsIn, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003251 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3252 Log.v(TAG,
3253 "startAddAccountSession: accountType " + accountType
3254 + ", response " + response
3255 + ", authTokenType " + authTokenType
3256 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3257 + ", expectActivityLaunch " + expectActivityLaunch
3258 + ", caller's uid " + Binder.getCallingUid()
3259 + ", pid " + Binder.getCallingPid());
3260 }
3261 if (response == null) {
3262 throw new IllegalArgumentException("response is null");
3263 }
3264 if (accountType == null) {
3265 throw new IllegalArgumentException("accountType is null");
3266 }
3267
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003268 final int uid = Binder.getCallingUid();
3269 final int userId = UserHandle.getUserId(uid);
3270 if (!canUserModifyAccounts(userId, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003271 try {
3272 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3273 "User is not allowed to add an account!");
3274 } catch (RemoteException re) {
3275 }
3276 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3277 return;
3278 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003279 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003280 try {
3281 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3282 "User cannot modify accounts of this type (policy).");
3283 } catch (RemoteException re) {
3284 }
3285 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3286 userId);
3287 return;
3288 }
Sandra Kwan78812282015-11-04 11:19:47 -08003289 final int pid = Binder.getCallingPid();
Sandra Kwan78812282015-11-04 11:19:47 -08003290 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3291 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3292 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3293
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003294 // Check to see if the Password should be included to the caller.
3295 String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3296 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003297 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003298
Sandra Kwan78812282015-11-04 11:19:47 -08003299 long identityToken = clearCallingIdentity();
3300 try {
Hongming Jin368aa192016-07-29 14:29:54 -07003301 UserAccounts accounts = getUserAccounts(userId);
Sandra Kwan78812282015-11-04 11:19:47 -08003302 logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
3303 TABLE_ACCOUNTS, uid);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003304 new StartAccountSession(
3305 accounts,
3306 response,
3307 accountType,
3308 expectActivityLaunch,
3309 null /* accountName */,
3310 false /* authDetailsRequired */,
3311 true /* updateLastAuthenticationTime */,
3312 isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003313 @Override
3314 public void run() throws RemoteException {
3315 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
3316 requiredFeatures, options);
3317 }
3318
3319 @Override
3320 protected String toDebugString(long now) {
3321 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
3322 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
3323 + accountType + ", requiredFeatures "
3324 + (requiredFeatures != null ? requiredFeaturesStr : null);
3325 }
3326 }.bind();
3327 } finally {
3328 restoreCallingIdentity(identityToken);
3329 }
3330 }
3331
3332 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
3333 private abstract class StartAccountSession extends Session {
3334
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003335 private final boolean mIsPasswordForwardingAllowed;
3336
3337 public StartAccountSession(
3338 UserAccounts accounts,
3339 IAccountManagerResponse response,
3340 String accountType,
3341 boolean expectActivityLaunch,
3342 String accountName,
3343 boolean authDetailsRequired,
3344 boolean updateLastAuthenticationTime,
3345 boolean isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003346 super(accounts, response, accountType, expectActivityLaunch,
3347 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
3348 updateLastAuthenticationTime);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003349 mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
Sandra Kwan78812282015-11-04 11:19:47 -08003350 }
3351
3352 @Override
3353 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003354 Bundle.setDefusable(result, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003355 mNumResults++;
3356 Intent intent = null;
Sandra Kwan78812282015-11-04 11:19:47 -08003357 if (result != null
3358 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08003359 checkKeyIntent(
3360 Binder.getCallingUid(),
3361 intent);
Sandra Kwan78812282015-11-04 11:19:47 -08003362 }
Sandra Kwan78812282015-11-04 11:19:47 -08003363 IAccountManagerResponse response;
3364 if (mExpectActivityLaunch && result != null
3365 && result.containsKey(AccountManager.KEY_INTENT)) {
3366 response = mResponse;
3367 } else {
3368 response = getResponseAndClose();
3369 }
3370 if (response == null) {
3371 return;
3372 }
3373 if (result == null) {
3374 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3375 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
3376 + response);
3377 }
3378 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3379 "null bundle returned");
3380 return;
3381 }
3382
3383 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
3384 // All AccountManager error codes are greater
3385 // than 0
3386 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
3387 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3388 return;
3389 }
3390
Hongming Jin368aa192016-07-29 14:29:54 -07003391 // Omit passwords if the caller isn't permitted to see them.
3392 if (!mIsPasswordForwardingAllowed) {
3393 result.remove(AccountManager.KEY_PASSWORD);
3394 }
3395
Sandra Kwan78812282015-11-04 11:19:47 -08003396 // Strip auth token from result.
3397 result.remove(AccountManager.KEY_AUTHTOKEN);
3398
3399 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3400 Log.v(TAG,
3401 getClass().getSimpleName() + " calling onResult() on response " + response);
3402 }
3403
3404 // Get the session bundle created by authenticator. The
3405 // bundle contains data necessary for finishing the session
3406 // later. The session bundle will be encrypted here and
3407 // decrypted later when trying to finish the session.
3408 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
3409 if (sessionBundle != null) {
3410 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3411 if (TextUtils.isEmpty(accountType)
Andreas Gampe9b041742015-12-11 17:23:33 -08003412 || !mAccountType.equalsIgnoreCase(accountType)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003413 Log.w(TAG, "Account type in session bundle doesn't match request.");
3414 }
3415 // Add accountType info to session bundle. This will
3416 // override any value set by authenticator.
3417 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
3418
3419 // Encrypt session bundle before returning to caller.
3420 try {
3421 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3422 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
3423 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
3424 } catch (GeneralSecurityException e) {
3425 if (Log.isLoggable(TAG, Log.DEBUG)) {
3426 Log.v(TAG, "Failed to encrypt session bundle!", e);
3427 }
3428 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3429 "failed to encrypt session bundle");
3430 return;
3431 }
3432 }
3433
3434 sendResponse(response, result);
3435 }
3436 }
3437
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003438 @Override
Sandra Kwan0b84b452016-01-20 15:25:42 -08003439 public void finishSessionAsUser(IAccountManagerResponse response,
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003440 @NonNull Bundle sessionBundle,
3441 boolean expectActivityLaunch,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003442 Bundle appInfo,
3443 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003444 Bundle.setDefusable(sessionBundle, true);
Sandra Kwan0b84b452016-01-20 15:25:42 -08003445 int callingUid = Binder.getCallingUid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003446 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3447 Log.v(TAG,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003448 "finishSession: response "+ response
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003449 + ", expectActivityLaunch " + expectActivityLaunch
Sandra Kwan0b84b452016-01-20 15:25:42 -08003450 + ", caller's uid " + callingUid
3451 + ", caller's user id " + UserHandle.getCallingUserId()
3452 + ", pid " + Binder.getCallingPid()
3453 + ", for user id " + userId);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003454 }
3455 if (response == null) {
3456 throw new IllegalArgumentException("response is null");
3457 }
3458
3459 // Session bundle is the encrypted bundle of the original bundle created by authenticator.
3460 // Account type is added to it before encryption.
3461 if (sessionBundle == null || sessionBundle.size() == 0) {
3462 throw new IllegalArgumentException("sessionBundle is empty");
3463 }
3464
Sandra Kwan0b84b452016-01-20 15:25:42 -08003465 // Only allow the system process to finish session for other users
3466 if (isCrossUser(callingUid, userId)) {
3467 throw new SecurityException(
3468 String.format(
3469 "User %s trying to finish session for %s without cross user permission",
3470 UserHandle.getCallingUserId(),
3471 userId));
3472 }
3473
Sandra Kwan0b84b452016-01-20 15:25:42 -08003474 if (!canUserModifyAccounts(userId, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003475 sendErrorResponse(response,
3476 AccountManager.ERROR_CODE_USER_RESTRICTED,
3477 "User is not allowed to add an account!");
3478 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3479 return;
3480 }
3481
3482 final int pid = Binder.getCallingPid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003483 final Bundle decryptedBundle;
3484 final String accountType;
3485 // First decrypt session bundle to get account type for checking permission.
3486 try {
3487 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3488 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
3489 if (decryptedBundle == null) {
3490 sendErrorResponse(
3491 response,
3492 AccountManager.ERROR_CODE_BAD_REQUEST,
3493 "failed to decrypt session bundle");
3494 return;
3495 }
3496 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3497 // Account type cannot be null. This should not happen if session bundle was created
3498 // properly by #StartAccountSession.
3499 if (TextUtils.isEmpty(accountType)) {
3500 sendErrorResponse(
3501 response,
3502 AccountManager.ERROR_CODE_BAD_ARGUMENTS,
3503 "accountType is empty");
3504 return;
3505 }
3506
3507 // If by any chances, decryptedBundle contains colliding keys with
3508 // system info
3509 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
3510 // update credentials flow, we should replace with the new values of the current call.
3511 if (appInfo != null) {
3512 decryptedBundle.putAll(appInfo);
3513 }
3514
3515 // Add info that may be used by add account or update credentials flow.
Sandra Kwan0b84b452016-01-20 15:25:42 -08003516 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003517 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
3518 } catch (GeneralSecurityException e) {
3519 if (Log.isLoggable(TAG, Log.DEBUG)) {
3520 Log.v(TAG, "Failed to decrypt session bundle!", e);
3521 }
3522 sendErrorResponse(
3523 response,
3524 AccountManager.ERROR_CODE_BAD_REQUEST,
3525 "failed to decrypt session bundle");
3526 return;
3527 }
3528
Sandra Kwan0b84b452016-01-20 15:25:42 -08003529 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003530 sendErrorResponse(
3531 response,
3532 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3533 "User cannot modify accounts of this type (policy).");
3534 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3535 userId);
3536 return;
3537 }
3538
3539 long identityToken = clearCallingIdentity();
3540 try {
3541 UserAccounts accounts = getUserAccounts(userId);
3542 logRecordWithUid(
3543 accounts,
3544 DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
3545 TABLE_ACCOUNTS,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003546 callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003547 new Session(
3548 accounts,
3549 response,
3550 accountType,
3551 expectActivityLaunch,
3552 true /* stripAuthTokenFromResult */,
3553 null /* accountName */,
3554 false /* authDetailsRequired */,
3555 true /* updateLastAuthenticationTime */) {
3556 @Override
3557 public void run() throws RemoteException {
3558 mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
3559 }
3560
3561 @Override
3562 protected String toDebugString(long now) {
3563 return super.toDebugString(now)
3564 + ", finishSession"
3565 + ", accountType " + accountType;
3566 }
3567 }.bind();
3568 } finally {
3569 restoreCallingIdentity(identityToken);
3570 }
3571 }
3572
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003573 private void showCantAddAccount(int errorCode, int userId) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003574 final DevicePolicyManagerInternal dpmi =
3575 LocalServices.getService(DevicePolicyManagerInternal.class);
3576 Intent intent = null;
Nicolas Prevot14fc1972016-08-24 14:21:38 +01003577 if (dpmi == null) {
3578 intent = getDefaultCantAddAccountIntent(errorCode);
3579 } else if (errorCode == AccountManager.ERROR_CODE_USER_RESTRICTED) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003580 intent = dpmi.createUserRestrictionSupportIntent(userId,
3581 UserManager.DISALLOW_MODIFY_ACCOUNTS);
3582 } else if (errorCode == AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
3583 intent = dpmi.createShowAdminSupportIntent(userId, false);
3584 }
3585 if (intent == null) {
3586 intent = getDefaultCantAddAccountIntent(errorCode);
3587 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003588 long identityToken = clearCallingIdentity();
3589 try {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003590 mContext.startActivityAsUser(intent, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003591 } finally {
3592 restoreCallingIdentity(identityToken);
3593 }
3594 }
3595
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003596 /**
3597 * Called when we don't know precisely who is preventing us from adding an account.
3598 */
3599 private Intent getDefaultCantAddAccountIntent(int errorCode) {
3600 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
3601 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
3602 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3603 return cantAddAccount;
3604 }
3605
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003606 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003607 public void confirmCredentialsAsUser(
3608 IAccountManagerResponse response,
3609 final Account account,
3610 final Bundle options,
3611 final boolean expectActivityLaunch,
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003612 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003613 Bundle.setDefusable(options, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003614 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003615 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3616 Log.v(TAG, "confirmCredentials: " + account
3617 + ", response " + response
3618 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003619 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003620 + ", pid " + Binder.getCallingPid());
3621 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003622 // Only allow the system process to read accounts of other users
3623 if (isCrossUser(callingUid, userId)) {
3624 throw new SecurityException(
3625 String.format(
3626 "User %s trying to confirm account credentials for %s" ,
3627 UserHandle.getCallingUserId(),
3628 userId));
3629 }
Fred Quintana382601f2010-03-25 12:25:10 -07003630 if (response == null) throw new IllegalArgumentException("response is null");
3631 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003632 long identityToken = clearCallingIdentity();
3633 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003634 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003635 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003636 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003637 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003638 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003639 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003640 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003641 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003642 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003643 protected String toDebugString(long now) {
3644 return super.toDebugString(now) + ", confirmCredentials"
3645 + ", " + account;
3646 }
3647 }.bind();
3648 } finally {
3649 restoreCallingIdentity(identityToken);
3650 }
Fred Quintana60307342009-03-24 22:48:12 -07003651 }
3652
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003653 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003654 public void updateCredentials(IAccountManagerResponse response, final Account account,
3655 final String authTokenType, final boolean expectActivityLaunch,
3656 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003657 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003658 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3659 Log.v(TAG, "updateCredentials: " + account
3660 + ", response " + response
3661 + ", authTokenType " + authTokenType
3662 + ", expectActivityLaunch " + expectActivityLaunch
3663 + ", caller's uid " + Binder.getCallingUid()
3664 + ", pid " + Binder.getCallingPid());
3665 }
Fred Quintana382601f2010-03-25 12:25:10 -07003666 if (response == null) throw new IllegalArgumentException("response is null");
3667 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003668 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003669 long identityToken = clearCallingIdentity();
3670 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003671 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003672 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003673 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003674 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003675 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003676 public void run() throws RemoteException {
3677 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
3678 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003679 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003680 protected String toDebugString(long now) {
3681 if (loginOptions != null) loginOptions.keySet();
3682 return super.toDebugString(now) + ", updateCredentials"
3683 + ", " + account
3684 + ", authTokenType " + authTokenType
3685 + ", loginOptions " + loginOptions;
3686 }
3687 }.bind();
3688 } finally {
3689 restoreCallingIdentity(identityToken);
3690 }
Fred Quintana60307342009-03-24 22:48:12 -07003691 }
3692
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003693 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003694 public void startUpdateCredentialsSession(
3695 IAccountManagerResponse response,
3696 final Account account,
3697 final String authTokenType,
3698 final boolean expectActivityLaunch,
3699 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003700 Bundle.setDefusable(loginOptions, true);
Sandra Kwane68c37e2015-11-12 17:11:49 -08003701 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3702 Log.v(TAG,
3703 "startUpdateCredentialsSession: " + account + ", response " + response
3704 + ", authTokenType " + authTokenType + ", expectActivityLaunch "
3705 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
3706 + ", pid " + Binder.getCallingPid());
3707 }
3708 if (response == null) {
3709 throw new IllegalArgumentException("response is null");
3710 }
3711 if (account == null) {
3712 throw new IllegalArgumentException("account is null");
3713 }
Sandra Kwana578d112015-12-16 16:01:43 -08003714
3715 final int uid = Binder.getCallingUid();
Sandra Kwane68c37e2015-11-12 17:11:49 -08003716 int userId = UserHandle.getCallingUserId();
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003717
3718 // Check to see if the Password should be included to the caller.
3719 String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3720 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003721 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003722
Sandra Kwane68c37e2015-11-12 17:11:49 -08003723 long identityToken = clearCallingIdentity();
3724 try {
3725 UserAccounts accounts = getUserAccounts(userId);
3726 new StartAccountSession(
3727 accounts,
3728 response,
3729 account.type,
3730 expectActivityLaunch,
3731 account.name,
3732 false /* authDetailsRequired */,
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003733 true /* updateLastCredentialTime */,
3734 isPasswordForwardingAllowed) {
Sandra Kwane68c37e2015-11-12 17:11:49 -08003735 @Override
3736 public void run() throws RemoteException {
3737 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
3738 loginOptions);
3739 }
3740
3741 @Override
3742 protected String toDebugString(long now) {
3743 if (loginOptions != null)
3744 loginOptions.keySet();
3745 return super.toDebugString(now)
3746 + ", startUpdateCredentialsSession"
3747 + ", " + account
3748 + ", authTokenType " + authTokenType
3749 + ", loginOptions " + loginOptions;
3750 }
3751 }.bind();
3752 } finally {
3753 restoreCallingIdentity(identityToken);
3754 }
3755 }
3756
3757 @Override
Sandra Kwan390c9d22016-01-12 14:13:37 -08003758 public void isCredentialsUpdateSuggested(
3759 IAccountManagerResponse response,
3760 final Account account,
3761 final String statusToken) {
3762 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3763 Log.v(TAG,
3764 "isCredentialsUpdateSuggested: " + account + ", response " + response
3765 + ", caller's uid " + Binder.getCallingUid()
3766 + ", pid " + Binder.getCallingPid());
3767 }
3768 if (response == null) {
3769 throw new IllegalArgumentException("response is null");
3770 }
3771 if (account == null) {
3772 throw new IllegalArgumentException("account is null");
3773 }
3774 if (TextUtils.isEmpty(statusToken)) {
3775 throw new IllegalArgumentException("status token is empty");
3776 }
3777
Sandra Kwan390c9d22016-01-12 14:13:37 -08003778 int usrId = UserHandle.getCallingUserId();
3779 long identityToken = clearCallingIdentity();
3780 try {
3781 UserAccounts accounts = getUserAccounts(usrId);
3782 new Session(accounts, response, account.type, false /* expectActivityLaunch */,
3783 false /* stripAuthTokenFromResult */, account.name,
3784 false /* authDetailsRequired */) {
3785 @Override
3786 protected String toDebugString(long now) {
3787 return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
3788 + ", " + account;
3789 }
3790
3791 @Override
3792 public void run() throws RemoteException {
3793 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
3794 }
3795
3796 @Override
3797 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003798 Bundle.setDefusable(result, true);
Sandra Kwan390c9d22016-01-12 14:13:37 -08003799 IAccountManagerResponse response = getResponseAndClose();
3800 if (response == null) {
3801 return;
3802 }
3803
3804 if (result == null) {
3805 sendErrorResponse(
3806 response,
3807 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3808 "null bundle");
3809 return;
3810 }
3811
3812 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3813 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3814 + response);
3815 }
3816 // Check to see if an error occurred. We know if an error occurred because all
3817 // error codes are greater than 0.
3818 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
3819 sendErrorResponse(response,
3820 result.getInt(AccountManager.KEY_ERROR_CODE),
3821 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3822 return;
3823 }
3824 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
3825 sendErrorResponse(
3826 response,
3827 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3828 "no result in response");
3829 return;
3830 }
3831 final Bundle newResult = new Bundle();
3832 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
3833 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
3834 sendResponse(response, newResult);
3835 }
3836 }.bind();
3837 } finally {
3838 restoreCallingIdentity(identityToken);
3839 }
3840 }
3841
3842 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003843 public void editProperties(IAccountManagerResponse response, final String accountType,
3844 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003845 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003846 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3847 Log.v(TAG, "editProperties: accountType " + accountType
3848 + ", response " + response
3849 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003850 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003851 + ", pid " + Binder.getCallingPid());
3852 }
Fred Quintana382601f2010-03-25 12:25:10 -07003853 if (response == null) throw new IllegalArgumentException("response is null");
3854 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003855 int userId = UserHandle.getCallingUserId();
3856 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003857 String msg = String.format(
3858 "uid %s cannot edit authenticator properites for account type: %s",
3859 callingUid,
3860 accountType);
3861 throw new SecurityException(msg);
3862 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003863 long identityToken = clearCallingIdentity();
3864 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003865 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003866 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003867 true /* stripAuthTokenFromResult */, null /* accountName */,
3868 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003869 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003870 public void run() throws RemoteException {
3871 mAuthenticator.editProperties(this, mAccountType);
3872 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003873 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003874 protected String toDebugString(long now) {
3875 return super.toDebugString(now) + ", editProperties"
3876 + ", accountType " + accountType;
3877 }
3878 }.bind();
3879 } finally {
3880 restoreCallingIdentity(identityToken);
3881 }
Fred Quintana60307342009-03-24 22:48:12 -07003882 }
3883
Amith Yamasani12747872015-12-07 14:19:49 -08003884 @Override
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003885 public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName,
3886 @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003887 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003888 throw new SecurityException("Can be called only by system UID");
3889 }
3890 Preconditions.checkNotNull(account, "account cannot be null");
3891 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3892 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3893
3894 final int userId = userHandle.getIdentifier();
3895
3896 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3897
3898 try {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003899 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
Svet Ganovf6d424f12016-09-20 20:18:53 -07003900 return hasAccountAccess(account, packageName, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003901 } catch (NameNotFoundException e) {
3902 return false;
3903 }
3904 }
3905
Svet Ganovf6d424f12016-09-20 20:18:53 -07003906 private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
3907 int uid) {
3908 if (packageName == null) {
3909 String[] packageNames = mPackageManager.getPackagesForUid(uid);
3910 if (ArrayUtils.isEmpty(packageNames)) {
3911 return false;
3912 }
3913 // For app op checks related to permissions all packages in the UID
3914 // have the same app op state, so doesn't matter which one we pick.
3915 packageName = packageNames[0];
3916 }
3917
3918 // Use null token which means any token. Having a token means the package
3919 // is trusted by the authenticator, hence it is fine to access the account.
3920 if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) {
3921 return true;
3922 }
3923 // In addition to the permissions required to get an auth token we also allow
3924 // the account to be accessed by holders of the get accounts permissions.
3925 return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
3926 || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
3927 }
3928
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003929 private boolean checkUidPermission(String permission, int uid, String opPackageName) {
3930 final long identity = Binder.clearCallingIdentity();
3931 try {
3932 IPackageManager pm = ActivityThread.getPackageManager();
3933 if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
3934 return false;
3935 }
3936 final int opCode = AppOpsManager.permissionToOpCode(permission);
3937 return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
3938 opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
3939 } catch (RemoteException e) {
3940 /* ignore - local call */
3941 } finally {
3942 Binder.restoreCallingIdentity(identity);
3943 }
3944 return false;
3945 }
3946
3947 @Override
3948 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
3949 @NonNull String packageName, @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003950 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003951 throw new SecurityException("Can be called only by system UID");
3952 }
3953
3954 Preconditions.checkNotNull(account, "account cannot be null");
3955 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3956 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3957
3958 final int userId = userHandle.getIdentifier();
3959
3960 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3961
3962 final int uid;
3963 try {
3964 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3965 } catch (NameNotFoundException e) {
3966 Slog.e(TAG, "Unknown package " + packageName);
3967 return null;
3968 }
3969
3970 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
3971
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003972 final long identity = Binder.clearCallingIdentity();
3973 try {
3974 return PendingIntent.getActivityAsUser(
3975 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
3976 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3977 null, new UserHandle(userId)).getIntentSender();
3978 } finally {
3979 Binder.restoreCallingIdentity(identity);
3980 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003981 }
3982
3983 private Intent newRequestAccountAccessIntent(Account account, String packageName,
3984 int uid, RemoteCallback callback) {
3985 return newGrantCredentialsPermissionIntent(account, packageName, uid,
3986 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
3987 @Override
3988 public void onResult(Bundle value) throws RemoteException {
3989 handleAuthenticatorResponse(true);
3990 }
3991
3992 @Override
3993 public void onRequestContinued() {
3994 /* ignore */
3995 }
3996
3997 @Override
3998 public void onError(int errorCode, String errorMessage) throws RemoteException {
3999 handleAuthenticatorResponse(false);
4000 }
4001
4002 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
4003 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -07004004 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004005 UserHandle.getUserHandleForUid(uid));
4006 if (callback != null) {
4007 Bundle result = new Bundle();
4008 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
4009 callback.sendResult(result);
4010 }
4011 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07004012 }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004013 }
4014
4015 @Override
Amith Yamasani12747872015-12-07 14:19:49 -08004016 public boolean someUserHasAccount(@NonNull final Account account) {
4017 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
4018 throw new SecurityException("Only system can check for accounts across users");
4019 }
4020 final long token = Binder.clearCallingIdentity();
4021 try {
4022 AccountAndUser[] allAccounts = getAllAccounts();
4023 for (int i = allAccounts.length - 1; i >= 0; i--) {
4024 if (allAccounts[i].account.equals(account)) {
4025 return true;
4026 }
4027 }
4028 return false;
4029 } finally {
4030 Binder.restoreCallingIdentity(token);
4031 }
4032 }
4033
Fred Quintana33269202009-04-20 16:05:10 -07004034 private class GetAccountsByTypeAndFeatureSession extends Session {
4035 private final String[] mFeatures;
4036 private volatile Account[] mAccountsOfType = null;
4037 private volatile ArrayList<Account> mAccountsWithFeatures = null;
4038 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004039 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07004040
Amith Yamasani04e0d262012-02-14 11:50:53 -08004041 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004042 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004043 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004044 true /* stripAuthTokenFromResult */, null /* accountName */,
4045 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004046 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07004047 mFeatures = features;
4048 }
4049
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004050 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004051 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004052 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004053 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
4054 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004055 }
Fred Quintana33269202009-04-20 16:05:10 -07004056 // check whether each account matches the requested features
Tejas Khorana5edff3b2016-06-28 20:59:52 -07004057 mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
Fred Quintana33269202009-04-20 16:05:10 -07004058 mCurrentAccount = 0;
4059
4060 checkAccount();
4061 }
4062
4063 public void checkAccount() {
4064 if (mCurrentAccount >= mAccountsOfType.length) {
4065 sendResult();
4066 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07004067 }
Fred Quintana33269202009-04-20 16:05:10 -07004068
Fred Quintana29e94b82010-03-10 12:11:51 -08004069 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
4070 if (accountAuthenticator == null) {
4071 // It is possible that the authenticator has died, which is indicated by
4072 // mAuthenticator being set to null. If this happens then just abort.
4073 // There is no need to send back a result or error in this case since
4074 // that already happened when mAuthenticator was cleared.
4075 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4076 Log.v(TAG, "checkAccount: aborting session since we are no longer"
4077 + " connected to the authenticator, " + toDebugString());
4078 }
4079 return;
4080 }
Fred Quintana33269202009-04-20 16:05:10 -07004081 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08004082 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07004083 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004084 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07004085 }
4086 }
4087
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004088 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004089 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004090 Bundle.setDefusable(result, true);
Fred Quintana33269202009-04-20 16:05:10 -07004091 mNumResults++;
4092 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004093 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07004094 return;
4095 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004096 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07004097 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
4098 }
4099 mCurrentAccount++;
4100 checkAccount();
4101 }
4102
4103 public void sendResult() {
4104 IAccountManagerResponse response = getResponseAndClose();
4105 if (response != null) {
4106 try {
4107 Account[] accounts = new Account[mAccountsWithFeatures.size()];
4108 for (int i = 0; i < accounts.length; i++) {
4109 accounts[i] = mAccountsWithFeatures.get(i);
4110 }
Fred Quintana56285a62010-12-02 14:20:51 -08004111 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4112 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
4113 + response);
4114 }
Fred Quintana33269202009-04-20 16:05:10 -07004115 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004116 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07004117 response.onResult(result);
4118 } catch (RemoteException e) {
4119 // if the caller is dead then there is no one to care about remote exceptions
4120 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4121 Log.v(TAG, "failure while notifying response", e);
4122 }
4123 }
4124 }
4125 }
4126
4127
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004128 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004129 protected String toDebugString(long now) {
4130 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
4131 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
4132 }
4133 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004134
Amith Yamasani04e0d262012-02-14 11:50:53 -08004135 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004136 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08004137 * @hide
4138 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004139 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004140 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004141 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07004142 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4143 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004144 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004145 return new Account[0];
4146 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004147 long identityToken = clearCallingIdentity();
4148 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004149 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004150 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004151 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004152 callingUid,
4153 null, // packageName
4154 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004155 } finally {
4156 restoreCallingIdentity(identityToken);
4157 }
4158 }
4159
Amith Yamasanif29f2362012-04-05 18:29:52 -07004160 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004161 * Returns accounts for all running users.
4162 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07004163 * @hide
4164 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004165 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004166 public AccountAndUser[] getRunningAccounts() {
4167 final int[] runningUserIds;
4168 try {
4169 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
4170 } catch (RemoteException e) {
4171 // Running in system_server; should never happen
4172 throw new RuntimeException(e);
4173 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004174 return getAccounts(runningUserIds);
4175 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07004176
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004177 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004178 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004179 public AccountAndUser[] getAllAccounts() {
Amith Yamasanid04aaa32016-06-13 12:09:36 -07004180 final List<UserInfo> users = getUserManager().getUsers(true);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004181 final int[] userIds = new int[users.size()];
4182 for (int i = 0; i < userIds.length; i++) {
4183 userIds[i] = users.get(i).id;
4184 }
4185 return getAccounts(userIds);
4186 }
4187
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004188 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004189 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004190 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004191 for (int userId : userIds) {
4192 UserAccounts userAccounts = getUserAccounts(userId);
4193 if (userAccounts == null) continue;
4194 synchronized (userAccounts.cacheLock) {
4195 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
4196 Binder.getCallingUid(), null);
4197 for (int a = 0; a < accounts.length; a++) {
4198 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07004199 }
4200 }
4201 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004202
4203 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
4204 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07004205 }
4206
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004207 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004208 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004209 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
4210 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004211 }
4212
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004213 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004214 private Account[] getAccountsAsUser(
4215 String type,
4216 int userId,
4217 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004218 int packageUid,
4219 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004220 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004221 // Only allow the system process to read accounts of other users
4222 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07004223 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08004224 && mContext.checkCallingOrSelfPermission(
4225 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
4226 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004227 throw new SecurityException("User " + UserHandle.getCallingUserId()
4228 + " trying to get account for " + userId);
4229 }
4230
Fred Quintana56285a62010-12-02 14:20:51 -08004231 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4232 Log.v(TAG, "getAccounts: accountType " + type
4233 + ", caller's uid " + Binder.getCallingUid()
4234 + ", pid " + Binder.getCallingPid());
4235 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004236 // If the original calling app was using the framework account chooser activity, we'll
4237 // be passed in the original caller's uid here, which is what should be used for filtering.
4238 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
4239 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07004240 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07004241 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004242 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4243 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004244 if (visibleAccountTypes.isEmpty()
4245 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004246 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004247 } else if (visibleAccountTypes.contains(type)) {
4248 // Prune the list down to just the requested type.
4249 visibleAccountTypes = new ArrayList<>();
4250 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004251 } // else aggregate all the visible accounts (it won't matter if the
4252 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004253
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004254 long identityToken = clearCallingIdentity();
4255 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004256 UserAccounts accounts = getUserAccounts(userId);
Tejas Khorana69990d92016-08-03 11:19:40 -07004257 Account[] accountsToReturn = getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004258 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004259 callingUid,
4260 callingPackage,
4261 visibleAccountTypes);
Tejas Khorana69990d92016-08-03 11:19:40 -07004262 ArrayList<Account> accountsToReturnList = new
4263 ArrayList<Account>(Arrays.asList(accountsToReturn));
4264 for(int i = accountsToReturnList.size() - 1; i >= 0 ; i--) {
4265 // if account not visible to caller or managed by caller, remove from
4266 // accounts to return. Note that all accounts visible by default unless
4267 // visible list functionality implemented
4268 if(!(isAccountVisible(accountsToReturnList.get(i), callingUid,
4269 getUserAccounts(userId)))) {
4270 accountsToReturnList.remove(i);
4271 }
4272 }
4273 return accountsToReturnList.toArray(new Account[accountsToReturnList.size()]);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004274 } finally {
4275 restoreCallingIdentity(identityToken);
4276 }
4277 }
4278
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004279 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004280 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004281 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004282 int callingUid,
4283 String callingPackage,
4284 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004285 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004286 ArrayList<Account> visibleAccounts = new ArrayList<>();
4287 for (String visibleType : visibleAccountTypes) {
4288 Account[] accountsForType = getAccountsFromCacheLocked(
4289 userAccounts, visibleType, callingUid, callingPackage);
4290 if (accountsForType != null) {
4291 visibleAccounts.addAll(Arrays.asList(accountsForType));
4292 }
4293 }
4294 Account[] result = new Account[visibleAccounts.size()];
4295 for (int i = 0; i < visibleAccounts.size(); i++) {
4296 result[i] = visibleAccounts.get(i);
4297 }
4298 return result;
4299 }
4300 }
4301
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004302 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004303 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07004304 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004305 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
4306 for (Account account : accounts) {
4307 addSharedAccountAsUser(account, userId);
4308 }
4309 }
4310
4311 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004312 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004313 UserAccounts accounts = getUserAccounts(userId);
4314 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004315 AccountsDbUtils.deleteSharedAccount(db, account);
4316 long accountId = AccountsDbUtils.insertSharedAccount(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004317 if (accountId < 0) {
4318 Log.w(TAG, "insertAccountIntoDatabase: " + account
4319 + ", skipping the DB insert failed");
4320 return false;
4321 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004322 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004323 return true;
4324 }
4325
4326 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004327 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
4328 userId = handleIncomingUser(userId);
4329 UserAccounts accounts = getUserAccounts(userId);
4330 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004331 long sharedTableAccountId = AccountsDbUtils.findSharedAccountId(db, account);
4332 int r = AccountsDbUtils.renameSharedAccount(db, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004333 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004334 int callingUid = getCallingUid();
4335 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
4336 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004337 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004338 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004339 }
4340 return r > 0;
4341 }
4342
4343 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08004344 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004345 return removeSharedAccountAsUser(account, userId, getCallingUid());
4346 }
4347
4348 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004349 userId = handleIncomingUser(userId);
4350 UserAccounts accounts = getUserAccounts(userId);
4351 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004352 long sharedTableAccountId = AccountsDbUtils.findSharedAccountId(db, account);
4353 boolean deleted = AccountsDbUtils.deleteSharedAccount(db, account);
4354 if (deleted) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004355 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
4356 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07004357 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004358 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004359 return deleted;
Amith Yamasani67df64b2012-12-14 12:09:36 -08004360 }
4361
4362 @Override
4363 public Account[] getSharedAccountsAsUser(int userId) {
4364 userId = handleIncomingUser(userId);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004365 SQLiteDatabase db = getUserAccounts(userId).openHelper.getReadableDatabase();
4366 List<Account> accountList = AccountsDbUtils.getSharedAccounts(db);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004367 Account[] accountArray = new Account[accountList.size()];
4368 accountList.toArray(accountArray);
4369 return accountArray;
4370 }
4371
4372 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004373 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004374 public Account[] getAccounts(String type, String opPackageName) {
Tejas Khorana69990d92016-08-03 11:19:40 -07004375 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004376 }
4377
Amith Yamasani27db4682013-03-30 17:07:47 -07004378 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004379 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004380 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004381 int callingUid = Binder.getCallingUid();
4382 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
4383 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
4384 + callingUid + " with uid=" + uid);
4385 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004386 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
4387 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004388 }
4389
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004390 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004391 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004392 public Account[] getAccountsByTypeForPackage(String type, String packageName,
4393 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004394 int packageUid = -1;
4395 try {
4396 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07004397 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
4398 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004399 } catch (RemoteException re) {
4400 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
4401 return new Account[0];
4402 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004403 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
4404 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004405 }
4406
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004407 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004408 public void getAccountsByFeatures(
4409 IAccountManagerResponse response,
4410 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004411 String[] features,
4412 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004413 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08004414 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4415 Log.v(TAG, "getAccounts: accountType " + type
4416 + ", response " + response
4417 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004418 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08004419 + ", pid " + Binder.getCallingPid());
4420 }
Fred Quintana382601f2010-03-25 12:25:10 -07004421 if (response == null) throw new IllegalArgumentException("response is null");
4422 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004423 int userId = UserHandle.getCallingUserId();
4424
Svetoslavf3f02ac2015-09-08 14:36:35 -07004425 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4426 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004427 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004428 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004429 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004430 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
4431 try {
4432 response.onResult(result);
4433 } catch (RemoteException e) {
4434 Log.e(TAG, "Cannot respond to caller do to exception." , e);
4435 }
4436 return;
4437 }
Fred Quintana33269202009-04-20 16:05:10 -07004438 long identityToken = clearCallingIdentity();
4439 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004440 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004441 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004442 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004443 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004444 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004445 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08004446 Bundle result = new Bundle();
4447 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4448 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004449 return;
4450 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004451 new GetAccountsByTypeAndFeatureSession(
4452 userAccounts,
4453 response,
4454 type,
4455 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004456 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07004457 } finally {
4458 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07004459 }
4460 }
4461
Fred Quintanaa698f422009-04-08 19:14:54 -07004462 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07004463 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07004464 IAccountManagerResponse mResponse;
4465 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004466 final boolean mExpectActivityLaunch;
4467 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004468 final String mAccountName;
4469 // Indicates if we need to add auth details(like last credential time)
4470 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004471 // If set, we need to update the last authenticated time. This is
4472 // currently
4473 // used on
4474 // successful confirming credentials.
4475 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07004476
Fred Quintana33269202009-04-20 16:05:10 -07004477 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07004478 private int mNumRequestContinued = 0;
4479 private int mNumErrors = 0;
4480
Fred Quintana60307342009-03-24 22:48:12 -07004481 IAccountAuthenticator mAuthenticator = null;
4482
Fred Quintana8570f742010-02-18 10:32:54 -08004483 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004484 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004485
Amith Yamasani04e0d262012-02-14 11:50:53 -08004486 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004487 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4488 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004489 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4490 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4491 }
4492
4493 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4494 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4495 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07004496 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08004497 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07004498 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08004499 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004500 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07004501 mResponse = response;
4502 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004503 mExpectActivityLaunch = expectActivityLaunch;
4504 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004505 mAccountName = accountName;
4506 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004507 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004508
Fred Quintanaa698f422009-04-08 19:14:54 -07004509 synchronized (mSessions) {
4510 mSessions.put(toString(), this);
4511 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004512 if (response != null) {
4513 try {
4514 response.asBinder().linkToDeath(this, 0 /* flags */);
4515 } catch (RemoteException e) {
4516 mResponse = null;
4517 binderDied();
4518 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004519 }
Fred Quintana60307342009-03-24 22:48:12 -07004520 }
4521
Fred Quintanaa698f422009-04-08 19:14:54 -07004522 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07004523 if (mResponse == null) {
4524 // this session has already been closed
4525 return null;
4526 }
Fred Quintana60307342009-03-24 22:48:12 -07004527 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07004528 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07004529 return response;
4530 }
4531
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004532 /**
4533 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4534 * security policy.
4535 *
4536 * In particular we want to make sure that the Authenticator doesn't try to trick users
4537 * into launching aribtrary intents on the device via by tricking to click authenticator
4538 * supplied entries in the system Settings app.
4539 */
4540 protected void checkKeyIntent(
4541 int authUid,
4542 Intent intent) throws SecurityException {
4543 long bid = Binder.clearCallingIdentity();
4544 try {
4545 PackageManager pm = mContext.getPackageManager();
4546 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4547 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4548 int targetUid = targetActivityInfo.applicationInfo.uid;
Sandra Kwan0e961a12016-06-30 14:34:01 -07004549 if (!GrantCredentialsPermissionActivity.class.getName().equals(
4550 targetActivityInfo.getClass().getName())
4551 && !CantAddAccountActivity.class
4552 .equals(targetActivityInfo.getClass().getName())
4553 && PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
4554 targetUid)) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004555 String pkgName = targetActivityInfo.packageName;
4556 String activityName = targetActivityInfo.name;
4557 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4558 + "does not share a signature with the supplying authenticator (%s).";
4559 throw new SecurityException(
4560 String.format(tmpl, activityName, pkgName, mAccountType));
4561 }
4562 } finally {
4563 Binder.restoreCallingIdentity(bid);
4564 }
4565 }
4566
Fred Quintanaa698f422009-04-08 19:14:54 -07004567 private void close() {
4568 synchronized (mSessions) {
4569 if (mSessions.remove(toString()) == null) {
4570 // the session was already closed, so bail out now
4571 return;
4572 }
4573 }
4574 if (mResponse != null) {
4575 // stop listening for response deaths
4576 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4577
4578 // clear this so that we don't accidentally send any further results
4579 mResponse = null;
4580 }
4581 cancelTimeout();
4582 unbind();
4583 }
4584
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004585 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004586 public void binderDied() {
4587 mResponse = null;
4588 close();
4589 }
4590
4591 protected String toDebugString() {
4592 return toDebugString(SystemClock.elapsedRealtime());
4593 }
4594
4595 protected String toDebugString(long now) {
4596 return "Session: expectLaunch " + mExpectActivityLaunch
4597 + ", connected " + (mAuthenticator != null)
4598 + ", stats (" + mNumResults + "/" + mNumRequestContinued
4599 + "/" + mNumErrors + ")"
4600 + ", lifetime " + ((now - mCreationTime) / 1000.0);
4601 }
4602
Fred Quintana60307342009-03-24 22:48:12 -07004603 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004604 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4605 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4606 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004607 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004608 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004609 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07004610 }
4611 }
4612
4613 private void unbind() {
4614 if (mAuthenticator != null) {
4615 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07004616 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07004617 }
4618 }
4619
Fred Quintana60307342009-03-24 22:48:12 -07004620 public void cancelTimeout() {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004621 mHandler.removeMessages(MESSAGE_TIMED_OUT, this);
Fred Quintana60307342009-03-24 22:48:12 -07004622 }
4623
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004624 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004625 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07004626 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07004627 try {
4628 run();
4629 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004630 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07004631 "remote exception");
4632 }
Fred Quintana60307342009-03-24 22:48:12 -07004633 }
4634
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004635 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004636 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004637 mAuthenticator = null;
4638 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004639 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004640 try {
4641 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4642 "disconnected");
4643 } catch (RemoteException e) {
4644 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4645 Log.v(TAG, "Session.onServiceDisconnected: "
4646 + "caught RemoteException while responding", e);
4647 }
4648 }
Fred Quintana60307342009-03-24 22:48:12 -07004649 }
4650 }
4651
Fred Quintanab839afc2009-10-14 15:57:28 -07004652 public abstract void run() throws RemoteException;
4653
Fred Quintana60307342009-03-24 22:48:12 -07004654 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004655 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004656 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004657 try {
4658 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4659 "timeout");
4660 } catch (RemoteException e) {
4661 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4662 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4663 e);
4664 }
4665 }
Fred Quintana60307342009-03-24 22:48:12 -07004666 }
4667 }
4668
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004669 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004670 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004671 Bundle.setDefusable(result, true);
Fred Quintanaa698f422009-04-08 19:14:54 -07004672 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004673 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004674 if (result != null) {
4675 boolean isSuccessfulConfirmCreds = result.getBoolean(
4676 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004677 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004678 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4679 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004680 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004681 // mUpdateLastAuthenticatedTime is true and the confirmRequest
4682 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07004683 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004684 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004685 if (needUpdate || mAuthDetailsRequired) {
4686 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4687 if (needUpdate && accountPresent) {
4688 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4689 }
4690 if (mAuthDetailsRequired) {
4691 long lastAuthenticatedTime = -1;
4692 if (accountPresent) {
4693 lastAuthenticatedTime = DatabaseUtils.longForQuery(
4694 mAccounts.openHelper.getReadableDatabase(),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004695 "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4696 + " FROM " +
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004697 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
4698 + ACCOUNTS_TYPE + "=?",
4699 new String[] {
4700 mAccountName, mAccountType
4701 });
4702 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07004703 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004704 lastAuthenticatedTime);
4705 }
4706 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004707 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004708 if (result != null
4709 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004710 checkKeyIntent(
4711 Binder.getCallingUid(),
4712 intent);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004713 }
4714 if (result != null
4715 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004716 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4717 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004718 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4719 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004720 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4721 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004722 }
Fred Quintana60307342009-03-24 22:48:12 -07004723 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004724 IAccountManagerResponse response;
4725 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004726 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004727 response = mResponse;
4728 } else {
4729 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004730 }
Fred Quintana60307342009-03-24 22:48:12 -07004731 if (response != null) {
4732 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07004733 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08004734 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4735 Log.v(TAG, getClass().getSimpleName()
4736 + " calling onError() on response " + response);
4737 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004738 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07004739 "null bundle returned");
4740 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08004741 if (mStripAuthTokenFromResult) {
4742 result.remove(AccountManager.KEY_AUTHTOKEN);
4743 }
Fred Quintana56285a62010-12-02 14:20:51 -08004744 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4745 Log.v(TAG, getClass().getSimpleName()
4746 + " calling onResult() on response " + response);
4747 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004748 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4749 (intent == null)) {
4750 // All AccountManager error codes are greater than 0
4751 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4752 result.getString(AccountManager.KEY_ERROR_MESSAGE));
4753 } else {
4754 response.onResult(result);
4755 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004756 }
Fred Quintana60307342009-03-24 22:48:12 -07004757 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004758 // if the caller is dead then there is no one to care about remote exceptions
4759 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4760 Log.v(TAG, "failure while notifying response", e);
4761 }
Fred Quintana60307342009-03-24 22:48:12 -07004762 }
4763 }
4764 }
Fred Quintana60307342009-03-24 22:48:12 -07004765
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004766 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004767 public void onRequestContinued() {
4768 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07004769 }
4770
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004771 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004772 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004773 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07004774 IAccountManagerResponse response = getResponseAndClose();
4775 if (response != null) {
4776 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08004777 Log.v(TAG, getClass().getSimpleName()
4778 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07004779 }
4780 try {
4781 response.onError(errorCode, errorMessage);
4782 } catch (RemoteException e) {
4783 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4784 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4785 }
4786 }
4787 } else {
4788 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4789 Log.v(TAG, "Session.onError: already closed");
4790 }
Fred Quintana60307342009-03-24 22:48:12 -07004791 }
4792 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004793
4794 /**
4795 * find the component name for the authenticator and initiate a bind
4796 * if no authenticator or the bind fails then return false, otherwise return true
4797 */
4798 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004799 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4800 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4801 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07004802 if (authenticatorInfo == null) {
4803 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4804 Log.v(TAG, "there is no authenticator for " + authenticatorType
4805 + ", bailing out");
4806 }
4807 return false;
4808 }
4809
Jeff Sharkeyce18c812016-04-27 16:00:41 -06004810 if (!isLocalUnlockedUser(mAccounts.userId)
Jeff Sharkey8a372a02016-03-16 16:25:45 -06004811 && !authenticatorInfo.componentInfo.directBootAware) {
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07004812 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4813 + " which isn't encryption aware");
4814 return false;
4815 }
4816
Fred Quintanab839afc2009-10-14 15:57:28 -07004817 Intent intent = new Intent();
4818 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4819 intent.setComponent(authenticatorInfo.componentName);
4820 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4821 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4822 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08004823 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004824 UserHandle.of(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07004825 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4826 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4827 }
4828 return false;
4829 }
4830
Fred Quintanab839afc2009-10-14 15:57:28 -07004831 return true;
4832 }
Fred Quintana60307342009-03-24 22:48:12 -07004833 }
4834
4835 private class MessageHandler extends Handler {
4836 MessageHandler(Looper looper) {
4837 super(looper);
4838 }
Costin Manolache3348f142009-09-29 18:58:36 -07004839
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004840 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004841 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07004842 switch (msg.what) {
4843 case MESSAGE_TIMED_OUT:
4844 Session session = (Session)msg.obj;
4845 session.onTimedOut();
4846 break;
4847
Amith Yamasani5be347b2013-03-31 17:44:31 -07004848 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00004849 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07004850 break;
4851
Fred Quintana60307342009-03-24 22:48:12 -07004852 default:
4853 throw new IllegalStateException("unhandled message: " + msg.what);
4854 }
4855 }
4856 }
4857
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004858 @VisibleForTesting
4859 String getPreNDatabaseName(int userId) {
Jeff Sharkey8212ae02016-02-10 14:46:43 -07004860 File systemDir = Environment.getDataSystemDirectory();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004861 File databaseFile = new File(Environment.getUserSystemDirectory(userId),
4862 PRE_N_DATABASE_NAME);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004863 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07004864 // Migrate old file, if it exists, to the new location.
4865 // Make sure the new file doesn't already exist. A dummy file could have been
4866 // accidentally created in the old location, causing the new one to become corrupted
4867 // as well.
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004868 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07004869 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07004870 // Check for use directory; create if it doesn't exist, else renameTo will fail
Amith Yamasani61f57372012-08-31 12:12:28 -07004871 File userDir = Environment.getUserSystemDirectory(userId);
Marc Blankc6b0f992012-03-18 19:16:41 -07004872 if (!userDir.exists()) {
4873 if (!userDir.mkdirs()) {
4874 throw new IllegalStateException("User dir cannot be created: " + userDir);
4875 }
4876 }
4877 if (!oldFile.renameTo(databaseFile)) {
4878 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
4879 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004880 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08004881 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004882 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08004883 }
4884
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004885 @VisibleForTesting
4886 String getDeDatabaseName(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004887 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
4888 DE_DATABASE_NAME);
4889 return databaseFile.getPath();
4890 }
4891
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004892 @VisibleForTesting
4893 String getCeDatabaseName(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004894 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
4895 CE_DATABASE_NAME);
4896 return databaseFile.getPath();
4897 }
4898
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004899 private static class DebugDbHelper{
4900 private DebugDbHelper() {
4901 }
4902
4903 private static String TABLE_DEBUG = "debug_table";
4904
4905 // Columns for the table
4906 private static String ACTION_TYPE = "action_type";
4907 private static String TIMESTAMP = "time";
4908 private static String CALLER_UID = "caller_uid";
4909 private static String TABLE_NAME = "table_name";
4910 private static String KEY = "primary_key";
4911
4912 // These actions correspond to the occurrence of real actions. Since
4913 // these are called by the authenticators, the uid associated will be
4914 // of the authenticator.
4915 private static String ACTION_SET_PASSWORD = "action_set_password";
4916 private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
4917 private static String ACTION_ACCOUNT_ADD = "action_account_add";
4918 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
Fyodor Kupolov35f68082016-04-06 12:14:17 -07004919 private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004920 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
4921 private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
4922
4923 // These actions don't necessarily correspond to any action on
4924 // accountDb taking place. As an example, there might be a request for
4925 // addingAccount, which might not lead to addition of account on grounds
4926 // of bad authentication. We will still be logging it to keep track of
4927 // who called.
4928 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
4929 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
Fyodor Kupolov35f68082016-04-06 12:14:17 -07004930 private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004931
Sandra Kwan920f6ef2015-11-10 14:13:29 -08004932 //This action doesn't add account to accountdb. Account is only
4933 // added in finishSession which may be in a different user profile.
Sandra Kwan78812282015-11-04 11:19:47 -08004934 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
Sandra Kwan920f6ef2015-11-10 14:13:29 -08004935 private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
4936 "action_called_account_session_finish";
Sandra Kwan78812282015-11-04 11:19:47 -08004937
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004938 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4939
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004940 private static void createDebugTable(SQLiteDatabase db) {
4941 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
4942 + ACCOUNTS_ID + " INTEGER,"
4943 + ACTION_TYPE + " TEXT NOT NULL, "
4944 + TIMESTAMP + " DATETIME,"
4945 + CALLER_UID + " INTEGER NOT NULL,"
4946 + TABLE_NAME + " TEXT NOT NULL,"
4947 + KEY + " INTEGER PRIMARY KEY)");
4948 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
4949 }
4950 }
4951
4952 private void logRecord(UserAccounts accounts, String action, String tableName) {
4953 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4954 logRecord(db, action, tableName, -1, accounts);
4955 }
4956
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004957 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
4958 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4959 logRecord(db, action, tableName, -1, accounts, uid);
4960 }
4961
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004962 /*
4963 * This function receives an opened writable database.
4964 */
4965 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4966 UserAccounts userAccount) {
4967 logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
4968 }
4969
4970 /*
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004971 * This function receives an opened writable database and writes to it in a separate thread.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004972 */
4973 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4974 UserAccounts userAccount, int callingUid) {
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004975
4976 class LogRecordTask implements Runnable {
4977 private final String action;
4978 private final String tableName;
4979 private final long accountId;
4980 private final UserAccounts userAccount;
4981 private final int callingUid;
4982 private final long userDebugDbInsertionPoint;
4983
4984 LogRecordTask(final String action,
4985 final String tableName,
4986 final long accountId,
4987 final UserAccounts userAccount,
4988 final int callingUid,
4989 final long userDebugDbInsertionPoint) {
4990 this.action = action;
4991 this.tableName = tableName;
4992 this.accountId = accountId;
4993 this.userAccount = userAccount;
4994 this.callingUid = callingUid;
4995 this.userDebugDbInsertionPoint = userDebugDbInsertionPoint;
4996 }
4997
4998 public void run() {
4999 SQLiteStatement logStatement = userAccount.statementForLogging;
5000 logStatement.bindLong(1, accountId);
5001 logStatement.bindString(2, action);
5002 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
5003 logStatement.bindLong(4, callingUid);
5004 logStatement.bindString(5, tableName);
5005 logStatement.bindLong(6, userDebugDbInsertionPoint);
5006 logStatement.execute();
5007 logStatement.clearBindings();
5008 }
5009 }
5010
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07005011 LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
5012 callingUid, userAccount.debugDbInsertionPoint);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005013 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
5014 % MAX_DEBUG_DB_SIZE;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07005015 mHandler.post(logTask);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005016 }
5017
5018 /*
5019 * This should only be called once to compile the sql statement for logging
5020 * and to find the insertion point.
5021 */
5022 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
5023 UserAccounts userAccount) {
5024 // Initialize the count if not done earlier.
5025 int size = (int) getDebugTableRowCount(db);
5026 if (size >= MAX_DEBUG_DB_SIZE) {
5027 // Table is full, and we need to find the point where to insert.
5028 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
5029 } else {
5030 userAccount.debugDbInsertionPoint = size;
5031 }
5032 compileSqlStatementForLogging(db, userAccount);
5033 }
5034
5035 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
5036 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
5037 + " VALUES (?,?,?,?,?,?)";
5038 userAccount.statementForLogging = db.compileStatement(sql);
5039 }
5040
5041 private long getDebugTableRowCount(SQLiteDatabase db) {
5042 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
5043 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
5044 }
5045
5046 /*
5047 * Finds the row key where the next insertion should take place. This should
5048 * be invoked only if the table has reached its full capacity.
5049 */
5050 private long getDebugTableInsertionPoint(SQLiteDatabase db) {
5051 // This query finds the smallest timestamp value (and if 2 records have
5052 // same timestamp, the choose the lower id).
5053 String queryCountDebugDbRows = new StringBuilder()
5054 .append("SELECT ").append(DebugDbHelper.KEY)
5055 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
5056 .append(" ORDER BY ")
5057 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
5058 .append(" LIMIT 1")
5059 .toString();
5060 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
5061 }
5062
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005063 static class PreNDatabaseHelper extends SQLiteOpenHelper {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005064 private final Context mContext;
5065 private final int mUserId;
5066
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005067 public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
5068 super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005069 mContext = context;
5070 mUserId = userId;
Fred Quintana60307342009-03-24 22:48:12 -07005071 }
5072
5073 @Override
5074 public void onCreate(SQLiteDatabase db) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005075 // We use PreNDatabaseHelper only if pre-N db exists
5076 throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005077 }
5078
Amith Yamasani67df64b2012-12-14 12:09:36 -08005079 private void createSharedAccountsTable(SQLiteDatabase db) {
5080 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
5081 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5082 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5083 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5084 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5085 }
5086
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08005087 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
5088 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
5089 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
5090 }
5091
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005092 private void addOldAccountNameColumn(SQLiteDatabase db) {
5093 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
5094 }
5095
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005096 private void addDebugTable(SQLiteDatabase db) {
5097 DebugDbHelper.createDebugTable(db);
5098 }
5099
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005100 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07005101 db.execSQL(""
5102 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5103 + " BEGIN"
5104 + " DELETE FROM " + TABLE_AUTHTOKENS
5105 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5106 + " DELETE FROM " + TABLE_EXTRAS
5107 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005108 + " DELETE FROM " + TABLE_GRANTS
5109 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07005110 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07005111 }
5112
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005113 private void createGrantsTable(SQLiteDatabase db) {
5114 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
5115 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5116 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
5117 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
5118 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
5119 + "," + GRANTS_GRANTEE_UID + "))");
5120 }
5121
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005122 private void populateMetaTableWithAuthTypeAndUID(SQLiteDatabase db,
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005123 Map<String, Integer> authTypeAndUIDMap) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005124 for (Entry<String, Integer> entry : authTypeAndUIDMap.entrySet()) {
5125 AccountsDbUtils.insertMetaAuthTypeAndUid(db, entry.getKey(), entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005126 }
5127 }
5128
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005129 /**
5130 * Pre-N database may need an upgrade before splitting
5131 */
Fred Quintana60307342009-03-24 22:48:12 -07005132 @Override
5133 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07005134 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07005135
Fred Quintanaa698f422009-04-08 19:14:54 -07005136 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005137 // no longer need to do anything since the work is done
5138 // when upgrading from version 2
5139 oldVersion++;
5140 }
5141
5142 if (oldVersion == 2) {
5143 createGrantsTable(db);
5144 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
5145 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07005146 oldVersion++;
5147 }
Costin Manolache3348f142009-09-29 18:58:36 -07005148
5149 if (oldVersion == 3) {
5150 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
5151 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
5152 oldVersion++;
5153 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08005154
5155 if (oldVersion == 4) {
5156 createSharedAccountsTable(db);
5157 oldVersion++;
5158 }
5159
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005160 if (oldVersion == 5) {
5161 addOldAccountNameColumn(db);
5162 oldVersion++;
5163 }
5164
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08005165 if (oldVersion == 6) {
5166 addLastSuccessfullAuthenticatedTimeColumn(db);
5167 oldVersion++;
5168 }
5169
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005170 if (oldVersion == 7) {
5171 addDebugTable(db);
5172 oldVersion++;
5173 }
5174
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005175 if (oldVersion == 8) {
5176 populateMetaTableWithAuthTypeAndUID(
5177 db,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005178 getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005179 oldVersion++;
5180 }
5181
Amith Yamasani67df64b2012-12-14 12:09:36 -08005182 if (oldVersion != newVersion) {
5183 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5184 }
Fred Quintana60307342009-03-24 22:48:12 -07005185 }
5186
5187 @Override
5188 public void onOpen(SQLiteDatabase db) {
5189 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
5190 }
5191 }
5192
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005193 static class DeDatabaseHelper extends SQLiteOpenHelper {
5194
5195 private final int mUserId;
5196 private volatile boolean mCeAttached;
5197
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005198 private DeDatabaseHelper(Context context, int userId, String deDatabaseName) {
5199 super(context, deDatabaseName, null, DE_DATABASE_VERSION);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005200 mUserId = userId;
5201 }
5202
5203 /**
5204 * This call needs to be made while the mCacheLock is held. The way to
5205 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
5206 * @param db The database.
5207 */
5208 @Override
5209 public void onCreate(SQLiteDatabase db) {
5210 Log.i(TAG, "Creating DE database for user " + mUserId);
5211 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
5212 + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
5213 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5214 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5215 + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
5216 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
5217 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5218
5219 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
5220 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
5221 + META_VALUE + " TEXT)");
5222
5223 createGrantsTable(db);
5224 createSharedAccountsTable(db);
5225 createAccountsDeletionTrigger(db);
5226 DebugDbHelper.createDebugTable(db);
5227 }
5228
5229 private void createSharedAccountsTable(SQLiteDatabase db) {
5230 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
5231 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5232 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5233 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5234 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5235 }
5236
5237 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5238 db.execSQL(""
5239 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5240 + " BEGIN"
5241 + " DELETE FROM " + TABLE_GRANTS
5242 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5243 + " END");
5244 }
5245
5246 private void createGrantsTable(SQLiteDatabase db) {
5247 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
5248 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5249 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
5250 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
5251 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
5252 + "," + GRANTS_GRANTEE_UID + "))");
5253 }
5254
5255 @Override
5256 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5257 Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
5258
5259 if (oldVersion != newVersion) {
5260 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5261 }
5262 }
5263
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005264 public void attachCeDatabase(File ceDbFile) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005265 SQLiteDatabase db = getWritableDatabase();
5266 db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb");
5267 mCeAttached = true;
5268 }
5269
5270 public boolean isCeDatabaseAttached() {
5271 return mCeAttached;
5272 }
5273
5274
5275 public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
5276 if(!mCeAttached) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005277 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
5278 + " is still locked. CE database is not yet available.", new Throwable());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005279 }
5280 return super.getReadableDatabase();
5281 }
5282
5283 public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
5284 if(!mCeAttached) {
5285 Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005286 + " is still locked. CE database is not yet available.", new Throwable());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005287 }
5288 return super.getWritableDatabase();
5289 }
5290
5291 @Override
5292 public void onOpen(SQLiteDatabase db) {
5293 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
5294 }
5295
5296 private void migratePreNDbToDe(File preNDbFile) {
5297 Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
5298 SQLiteDatabase db = getWritableDatabase();
5299 db.execSQL("ATTACH DATABASE '" + preNDbFile.getPath() + "' AS preNDb");
5300 db.beginTransaction();
5301 // Copy accounts fields
5302 db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
5303 + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
5304 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
5305 + ") "
5306 + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
5307 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
5308 + " FROM preNDb." + TABLE_ACCOUNTS);
5309 // Copy SHARED_ACCOUNTS
5310 db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
5311 + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
5312 "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
5313 + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
5314 // Copy DEBUG_TABLE
5315 db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
5316 + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
5317 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
5318 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
5319 "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
5320 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
5321 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
5322 + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
5323 // Copy GRANTS
5324 db.execSQL("INSERT INTO " + TABLE_GRANTS
5325 + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
5326 + GRANTS_GRANTEE_UID + ") " +
5327 "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
5328 + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
5329 // Copy META
5330 db.execSQL("INSERT INTO " + TABLE_META
5331 + "(" + META_KEY + "," + META_VALUE + ") "
5332 + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
5333 db.setTransactionSuccessful();
5334 db.endTransaction();
5335
5336 db.execSQL("DETACH DATABASE preNDb");
5337 }
5338
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005339 static DeDatabaseHelper create(
5340 Context context,
5341 int userId,
5342 File preNDatabaseFile,
5343 File deDatabaseFile) {
5344 boolean newDbExists = deDatabaseFile.exists();
5345 DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
5346 deDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005347 // If the db just created, and there is a legacy db, migrate it
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005348 if (!newDbExists && preNDatabaseFile.exists()) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005349 // Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005350 PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
5351 preNDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005352 // Open the database to force upgrade if required
5353 preNDatabaseHelper.getWritableDatabase();
5354 preNDatabaseHelper.close();
5355 // Move data without SPII to DE
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005356 deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005357 }
5358 return deDatabaseHelper;
5359 }
5360 }
5361
5362 static class CeDatabaseHelper extends SQLiteOpenHelper {
5363
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005364 public CeDatabaseHelper(Context context, String ceDatabaseName) {
5365 super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005366 }
5367
5368 /**
5369 * This call needs to be made while the mCacheLock is held.
5370 * @param db The database.
5371 */
5372 @Override
5373 public void onCreate(SQLiteDatabase db) {
5374 Log.i(TAG, "Creating CE database " + getDatabaseName());
5375 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
5376 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5377 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5378 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5379 + ACCOUNTS_PASSWORD + " TEXT, "
5380 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5381
5382 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
5383 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5384 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5385 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
5386 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
5387 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
5388
5389 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
5390 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5391 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
5392 + EXTRAS_KEY + " TEXT NOT NULL, "
5393 + EXTRAS_VALUE + " TEXT, "
5394 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
5395
5396 createAccountsDeletionTrigger(db);
5397 }
5398
5399 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5400 db.execSQL(""
5401 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5402 + " BEGIN"
5403 + " DELETE FROM " + TABLE_AUTHTOKENS
5404 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5405 + " DELETE FROM " + TABLE_EXTRAS
5406 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5407 + " END");
5408 }
5409
5410 @Override
5411 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5412 Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
5413
5414 if (oldVersion == 9) {
5415 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5416 Log.v(TAG, "onUpgrade upgrading to v10");
5417 }
5418 db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
5419 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
5420 // Recreate the trigger, since the old one references the table to be removed
5421 db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
5422 createAccountsDeletionTrigger(db);
5423 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
5424 db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
5425 oldVersion ++;
5426 }
5427
5428 if (oldVersion != newVersion) {
5429 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5430 }
5431 }
5432
5433 @Override
5434 public void onOpen(SQLiteDatabase db) {
5435 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
5436 }
5437
Fyodor Kupolov35f68082016-04-06 12:14:17 -07005438
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005439 /**
5440 * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
5441 * it also performs migration to the new CE database.
5442 * @param context
5443 * @param userId id of the user where the database is located
5444 */
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005445 static CeDatabaseHelper create(
5446 Context context,
5447 int userId,
5448 File preNDatabaseFile,
5449 File ceDatabaseFile) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005450 boolean newDbExists = ceDatabaseFile.exists();
5451 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5452 Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005453 + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005454 }
5455 boolean removeOldDb = false;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005456 if (!newDbExists && preNDatabaseFile.exists()) {
5457 removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005458 }
5459 // Try to open and upgrade if necessary
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005460 CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005461 ceHelper.getWritableDatabase();
5462 ceHelper.close();
5463 if (removeOldDb) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005464 Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile);
5465 if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) {
5466 Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile);
5467 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005468 }
5469 return ceHelper;
5470 }
5471
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005472 private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005473 Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005474 try {
5475 FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
5476 } catch (IOException e) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005477 Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005478 // Try to remove potentially damaged file if I/O error occurred
5479 deleteDbFileWarnIfFailed(ceDbFile);
5480 return false;
5481 }
5482 return true;
5483 }
5484 }
5485
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005486 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07005487 return asBinder();
5488 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005489
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005490 /**
5491 * Searches array of arguments for the specified string
5492 * @param args array of argument strings
5493 * @param value value to search for
5494 * @return true if the value is contained in the array
5495 */
5496 private static boolean scanArgs(String[] args, String value) {
5497 if (args != null) {
5498 for (String arg : args) {
5499 if (value.equals(arg)) {
5500 return true;
5501 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005502 }
5503 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005504 return false;
5505 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005506
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005507 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005508 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07005509 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
5510 != PackageManager.PERMISSION_GRANTED) {
5511 fout.println("Permission Denial: can't dump AccountsManager from from pid="
5512 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
5513 + " without permission " + android.Manifest.permission.DUMP);
5514 return;
5515 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005516 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005517 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07005518
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005519 final List<UserInfo> users = getUserManager().getUsers();
5520 for (UserInfo user : users) {
5521 ipw.println("User " + user + ":");
5522 ipw.increaseIndent();
5523 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
5524 ipw.println();
5525 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08005526 }
5527 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005528
Amith Yamasani04e0d262012-02-14 11:50:53 -08005529 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
5530 String[] args, boolean isCheckinRequest) {
5531 synchronized (userAccounts.cacheLock) {
5532 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005533
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005534 if (isCheckinRequest) {
5535 // This is a checkin request. *Only* upload the account types and the count of each.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005536 AccountsDbUtils.dumpAccountsTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005537 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005538 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07005539 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005540 fout.println("Accounts: " + accounts.length);
5541 for (Account account : accounts) {
5542 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005543 }
Fred Quintana307da1a2010-01-21 14:24:20 -08005544
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005545 // Add debug information.
5546 fout.println();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005547 AccountsDbUtils.dumpDebugTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005548 fout.println();
5549 synchronized (mSessions) {
5550 final long now = SystemClock.elapsedRealtime();
5551 fout.println("Active Sessions: " + mSessions.size());
5552 for (Session session : mSessions.values()) {
5553 fout.println(" " + session.toDebugString(now));
5554 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005555 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005556
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005557 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005558 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005559 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005560 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005561 }
5562
Amith Yamasani04e0d262012-02-14 11:50:53 -08005563 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005564 Intent intent, String packageName, final int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005565 long identityToken = clearCallingIdentity();
5566 try {
5567 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5568 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
5569 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005570
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005571 if (intent.getComponent() != null &&
5572 GrantCredentialsPermissionActivity.class.getName().equals(
5573 intent.getComponent().getClassName())) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005574 createNoCredentialsPermissionNotification(account, intent, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005575 } else {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005576 Context contextForUser = getContextForUser(new UserHandle(userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005577 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07005578 intent.addCategory(String.valueOf(notificationId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005579
Fred Quintana33f889a2009-09-14 17:31:26 -07005580 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01005581 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04005582 Notification n = new Notification.Builder(contextForUser)
5583 .setWhen(0)
5584 .setSmallIcon(android.R.drawable.stat_sys_warning)
5585 .setColor(contextForUser.getColor(
5586 com.android.internal.R.color.system_notification_accent_color))
5587 .setContentTitle(String.format(notificationTitleFormat, account.name))
5588 .setContentText(message)
5589 .setContentIntent(PendingIntent.getActivityAsUser(
5590 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005591 null, new UserHandle(userId)))
Chris Wren1ce4b6d2015-06-11 10:19:43 -04005592 .build();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005593 installNotification(notificationId, n, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005594 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005595 } finally {
5596 restoreCallingIdentity(identityToken);
5597 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005598 }
5599
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005600 @VisibleForTesting
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005601 protected void installNotification(int notificationId, final Notification notification,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005602 UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005603 installNotification(notificationId, notification, "android", user.getIdentifier());
5604 }
5605
5606 private void installNotification(int notificationId, final Notification notification,
5607 String packageName, int userId) {
5608 final long token = clearCallingIdentity();
5609 try {
5610 INotificationManager notificationManager = NotificationManager.getService();
5611 try {
5612 notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
5613 notificationId, notification, new int[1], userId);
5614 } catch (RemoteException e) {
5615 /* ignore - local call */
5616 }
5617 } finally {
5618 Binder.restoreCallingIdentity(token);
5619 }
Fred Quintana56285a62010-12-02 14:20:51 -08005620 }
5621
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005622 @VisibleForTesting
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005623 protected void cancelNotification(int id, UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005624 cancelNotification(id, mContext.getPackageName(), user);
5625 }
5626
5627 protected void cancelNotification(int id, String packageName, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005628 long identityToken = clearCallingIdentity();
5629 try {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005630 INotificationManager service = INotificationManager.Stub.asInterface(
5631 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
5632 service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
5633 } catch (RemoteException e) {
5634 /* ignore - local call */
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005635 } finally {
5636 restoreCallingIdentity(identityToken);
5637 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005638 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005639
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005640 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
5641 for (String perm : permissions) {
5642 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
5643 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5644 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
5645 }
5646 final int opCode = AppOpsManager.permissionToOpCode(perm);
5647 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
5648 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
5649 return true;
5650 }
5651 }
5652 }
5653 return false;
5654 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005655
Amith Yamasani67df64b2012-12-14 12:09:36 -08005656 private int handleIncomingUser(int userId) {
5657 try {
5658 return ActivityManagerNative.getDefault().handleIncomingUser(
5659 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
5660 } catch (RemoteException re) {
5661 // Shouldn't happen, local.
5662 }
5663 return userId;
5664 }
5665
Christopher Tateccbf84f2013-05-08 15:25:41 -07005666 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005667 final int callingUserId = UserHandle.getUserId(callingUid);
5668
5669 final PackageManager userPackageManager;
5670 try {
5671 userPackageManager = mContext.createPackageContextAsUser(
5672 "android", 0, new UserHandle(callingUserId)).getPackageManager();
5673 } catch (NameNotFoundException e) {
5674 return false;
5675 }
5676
5677 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07005678 for (String name : packages) {
5679 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005680 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08005681 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08005682 && (packageInfo.applicationInfo.privateFlags
5683 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07005684 return true;
5685 }
5686 } catch (PackageManager.NameNotFoundException e) {
5687 return false;
5688 }
5689 }
5690 return false;
5691 }
5692
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005693 private boolean permissionIsGranted(
5694 Account account, String authTokenType, int callerUid, int userId) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005695 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5696 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5697 Log.v(TAG, "Access to " + account + " granted calling uid is system");
5698 }
5699 return true;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005700 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005701
5702 if (isPrivileged(callerUid)) {
5703 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5704 Log.v(TAG, "Access to " + account + " granted calling uid "
5705 + callerUid + " privileged");
5706 }
5707 return true;
5708 }
5709 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
5710 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5711 Log.v(TAG, "Access to " + account + " granted calling uid "
5712 + callerUid + " manages the account");
5713 }
5714 return true;
5715 }
5716 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5717 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5718 Log.v(TAG, "Access to " + account + " granted calling uid "
5719 + callerUid + " user granted access");
5720 }
5721 return true;
5722 }
5723
5724 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5725 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5726 }
5727
5728 return false;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005729 }
5730
Svetoslavf3f02ac2015-09-08 14:36:35 -07005731 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5732 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005733 if (accountType == null) {
5734 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005735 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07005736 return getTypesVisibleToCaller(callingUid, userId,
5737 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005738 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005739 }
5740
5741 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5742 if (accountType == null) {
5743 return false;
5744 } else {
5745 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5746 }
5747 }
5748
Svetoslavf3f02ac2015-09-08 14:36:35 -07005749 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5750 String opPackageName) {
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005751 boolean isPermitted =
5752 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5753 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005754 return getTypesForCaller(callingUid, userId, isPermitted);
5755 }
5756
5757 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5758 return getTypesForCaller(callingUid, userId, false);
5759 }
5760
5761 private List<String> getTypesForCaller(
5762 int callingUid, int userId, boolean isOtherwisePermitted) {
5763 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005764 long identityToken = Binder.clearCallingIdentity();
5765 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5766 try {
5767 serviceInfos = mAuthenticatorCache.getAllServices(userId);
5768 } finally {
5769 Binder.restoreCallingIdentity(identityToken);
5770 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005771 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005772 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005773 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5774 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5775 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005776 }
5777 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005778 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005779 }
5780
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07005781 private boolean isAccountPresentForCaller(String accountName, String accountType) {
5782 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5783 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5784 if (account.name.equals(accountName)) {
5785 return true;
5786 }
5787 }
5788 }
5789 return false;
5790 }
5791
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07005792 private static void checkManageUsersPermission(String message) {
5793 if (ActivityManager.checkComponentPermission(
5794 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5795 != PackageManager.PERMISSION_GRANTED) {
5796 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5797 }
5798 }
5799
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07005800 private static void checkManageOrCreateUsersPermission(String message) {
5801 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5802 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5803 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5804 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5805 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5806 + message);
5807 }
5808 }
5809
Amith Yamasani04e0d262012-02-14 11:50:53 -08005810 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5811 int callerUid) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005812 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005813 return true;
5814 }
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005815 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005816 synchronized (accounts.cacheLock) {
5817 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005818 final String query;
5819 final String[] args;
5820
5821 if (authTokenType != null) {
5822 query = COUNT_OF_MATCHING_GRANTS;
5823 args = new String[] {String.valueOf(callerUid), authTokenType,
5824 account.name, account.type};
5825 } else {
5826 query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN;
5827 args = new String[] {String.valueOf(callerUid), account.name,
5828 account.type};
5829 }
5830 final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0;
Svet Ganov890a2102016-08-24 00:08:00 -07005831
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005832 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5833 // TODO: Skip this check when running automated tests. Replace this
5834 // with a more general solution.
5835 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08005836 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005837 + " but ignoring since device is in test harness.");
5838 return true;
5839 }
5840 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005841 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005842 }
5843
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005844 private boolean isSystemUid(int callingUid) {
5845 String[] packages = null;
5846 long ident = Binder.clearCallingIdentity();
5847 try {
5848 packages = mPackageManager.getPackagesForUid(callingUid);
5849 } finally {
5850 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005851 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005852 if (packages != null) {
5853 for (String name : packages) {
5854 try {
5855 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5856 if (packageInfo != null
5857 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5858 != 0) {
5859 return true;
5860 }
5861 } catch (PackageManager.NameNotFoundException e) {
5862 Log.w(TAG, String.format("Could not find package [%s]", name), e);
5863 }
5864 }
5865 } else {
5866 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005867 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005868 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00005869 }
5870
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005871 /** Succeeds if any of the specified permissions are granted. */
5872 private void checkReadAccountsPermitted(
5873 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005874 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07005875 int userId,
5876 String opPackageName) {
5877 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005878 String msg = String.format(
5879 "caller uid %s cannot access %s accounts",
5880 callingUid,
5881 accountType);
5882 Log.w(TAG, " " + msg);
5883 throw new SecurityException(msg);
5884 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005885 }
5886
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005887 private boolean canUserModifyAccounts(int userId, int callingUid) {
5888 // the managing app can always modify accounts
5889 if (isProfileOwner(callingUid)) {
5890 return true;
5891 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005892 if (getUserManager().getUserRestrictions(new UserHandle(userId))
5893 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5894 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005895 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005896 return true;
5897 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005898
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005899 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5900 // the managing app can always modify accounts
5901 if (isProfileOwner(callingUid)) {
5902 return true;
5903 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005904 DevicePolicyManager dpm = (DevicePolicyManager) mContext
5905 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005906 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02005907 if (typesArray == null) {
5908 return true;
5909 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005910 for (String forbiddenType : typesArray) {
5911 if (forbiddenType.equals(accountType)) {
5912 return false;
5913 }
5914 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005915 return true;
5916 }
5917
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005918 private boolean isProfileOwner(int uid) {
5919 final DevicePolicyManagerInternal dpmi =
5920 LocalServices.getService(DevicePolicyManagerInternal.class);
5921 return (dpmi != null)
5922 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5923 }
5924
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08005925 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07005926 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5927 throws RemoteException {
5928 final int callingUid = getCallingUid();
5929
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005930 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07005931 throw new SecurityException();
5932 }
5933
5934 if (value) {
5935 grantAppPermission(account, authTokenType, uid);
5936 } else {
5937 revokeAppPermission(account, authTokenType, uid);
5938 }
5939 }
5940
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005941 /**
5942 * Allow callers with the given uid permission to get credentials for account/authTokenType.
5943 * <p>
5944 * Although this is public it can only be accessed via the AccountManagerService object
5945 * which is in the system. This means we don't need to protect it with permissions.
5946 * @hide
5947 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005948 private void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005949 if (account == null || authTokenType == null) {
5950 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005951 return;
5952 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005953 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005954 synchronized (accounts.cacheLock) {
5955 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005956 long accountId = AccountsDbUtils.findAccountId(db, account);
5957 if (accountId >= 0) {
5958 AccountsDbUtils.insertGrant(db, accountId, authTokenType, uid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005959 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005960 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005961 UserHandle.of(accounts.userId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005962
5963 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005964 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005965
5966 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5967 for (AccountManagerInternal.OnAppPermissionChangeListener listener
5968 : mAppPermissionChangeListeners) {
5969 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5970 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005971 }
5972
5973 /**
5974 * Don't allow callers with the given uid permission to get credentials for
5975 * account/authTokenType.
5976 * <p>
5977 * Although this is public it can only be accessed via the AccountManagerService object
5978 * which is in the system. This means we don't need to protect it with permissions.
5979 * @hide
5980 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005981 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005982 if (account == null || authTokenType == null) {
5983 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005984 return;
5985 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005986 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005987 synchronized (accounts.cacheLock) {
5988 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005989 db.beginTransaction();
5990 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005991 long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005992 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005993 AccountsDbUtils.deleteGrantsByAccountIdAuthTokenTypeAndUid(
5994 db, accountId, authTokenType, uid);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005995 db.setTransactionSuccessful();
5996 }
5997 } finally {
5998 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005999 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07006000
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07006001 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
6002 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07006003 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07006004
6005 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
6006 for (AccountManagerInternal.OnAppPermissionChangeListener listener
6007 : mAppPermissionChangeListeners) {
6008 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
6009 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07006010 }
Fred Quintana56285a62010-12-02 14:20:51 -08006011
6012 static final private String stringArrayToString(String[] value) {
6013 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
6014 }
6015
Amith Yamasani04e0d262012-02-14 11:50:53 -08006016 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
6017 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006018 if (oldAccountsForType != null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006019 ArrayList<Account> newAccountsList = new ArrayList<>();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006020 for (Account curAccount : oldAccountsForType) {
6021 if (!curAccount.equals(account)) {
6022 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006023 }
6024 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006025 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08006026 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006027 } else {
6028 Account[] newAccountsForType = new Account[newAccountsList.size()];
6029 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006030 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006031 }
Fred Quintana56285a62010-12-02 14:20:51 -08006032 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08006033 accounts.userDataCache.remove(account);
6034 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07006035 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08006036 }
6037
6038 /**
6039 * This assumes that the caller has already checked that the account is not already present.
Svetoslav Ganov57f62592016-09-16 17:29:05 -07006040 * IMPORTANT: The account being inserted will begin to be tracked for access in remote
6041 * processes and if you will return this account to apps you should return the result.
6042 * @return The inserted account which is a new instance that is being tracked.
Fred Quintana56285a62010-12-02 14:20:51 -08006043 */
Svetoslav Ganov57f62592016-09-16 17:29:05 -07006044 private Account insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08006045 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006046 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
6047 Account[] newAccountsForType = new Account[oldLength + 1];
6048 if (accountsForType != null) {
6049 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08006050 }
Svetoslav Ganov57f62592016-09-16 17:29:05 -07006051 IAccountAccessTracker accessTracker = account.getAccessTracker() != null
6052 ? account.getAccessTracker() : new AccountAccessTracker();
6053 newAccountsForType[oldLength] = new Account(account, accessTracker);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006054 accounts.accountCache.put(account.type, newAccountsForType);
Svetoslav Ganov57f62592016-09-16 17:29:05 -07006055 return newAccountsForType[oldLength];
Fred Quintana56285a62010-12-02 14:20:51 -08006056 }
6057
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006058 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07006059 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006060 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07006061 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006062 return unfiltered;
6063 }
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07006064 UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
Amith Yamasani0c19bf52013-10-03 10:34:58 -07006065 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006066 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006067 // If any of the packages is a visible listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006068 // otherwise return non-shared accounts only.
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006069 // This might be a temporary way to specify a visible list
6070 String visibleList = mContext.getResources().getString(
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006071 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
6072 for (String packageName : packages) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006073 if (visibleList.contains(";" + packageName + ";")) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006074 return unfiltered;
6075 }
6076 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006077 ArrayList<Account> allowed = new ArrayList<>();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006078 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
6079 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006080 String requiredAccountType = "";
6081 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07006082 // If there's an explicit callingPackage specified, check if that package
6083 // opted in to see restricted accounts.
6084 if (callingPackage != null) {
6085 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006086 if (pi != null && pi.restrictedAccountType != null) {
6087 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07006088 }
6089 } else {
6090 // Otherwise check if the callingUid has a package that has opted in
6091 for (String packageName : packages) {
6092 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
6093 if (pi != null && pi.restrictedAccountType != null) {
6094 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07006095 break;
6096 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006097 }
6098 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006099 } catch (NameNotFoundException nnfe) {
6100 }
6101 for (Account account : unfiltered) {
6102 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006103 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006104 } else {
6105 boolean found = false;
6106 for (Account shared : sharedAccounts) {
6107 if (shared.equals(account)) {
6108 found = true;
6109 break;
6110 }
6111 }
6112 if (!found) {
6113 allowed.add(account);
6114 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006115 }
6116 }
6117 Account[] filtered = new Account[allowed.size()];
6118 allowed.toArray(filtered);
6119 return filtered;
6120 } else {
6121 return unfiltered;
6122 }
6123 }
6124
Amith Yamasani27db4682013-03-30 17:07:47 -07006125 /*
6126 * packageName can be null. If not null, it should be used to filter out restricted accounts
6127 * that the package is not allowed to access.
6128 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006129 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07006130 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006131 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08006132 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006133 if (accounts == null) {
6134 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08006135 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006136 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07006137 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08006138 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006139 } else {
6140 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006141 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006142 totalLength += accounts.length;
6143 }
6144 if (totalLength == 0) {
6145 return EMPTY_ACCOUNT_ARRAY;
6146 }
6147 Account[] accounts = new Account[totalLength];
6148 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006149 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006150 System.arraycopy(accountsOfType, 0, accounts, totalLength,
6151 accountsOfType.length);
6152 totalLength += accountsOfType.length;
6153 }
Amith Yamasani27db4682013-03-30 17:07:47 -07006154 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08006155 }
6156 }
6157
Amith Yamasani04e0d262012-02-14 11:50:53 -08006158 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
6159 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006160 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006161 if (userDataForAccount == null) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006162 userDataForAccount = AccountsDbUtils.findUserExtrasForAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006163 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006164 }
6165 if (value == null) {
6166 userDataForAccount.remove(key);
6167 } else {
6168 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08006169 }
6170 }
6171
Carlos Valdivia91979be2015-05-22 14:11:35 -07006172 protected String readCachedTokenInternal(
6173 UserAccounts accounts,
6174 Account account,
6175 String tokenType,
6176 String callingPackage,
6177 byte[] pkgSigDigest) {
6178 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07006179 return accounts.accountTokenCaches.get(
6180 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07006181 }
6182 }
6183
Amith Yamasani04e0d262012-02-14 11:50:53 -08006184 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
6185 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006186 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006187 if (authTokensForAccount == null) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006188 authTokensForAccount = AccountsDbUtils.findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006189 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006190 }
6191 if (value == null) {
6192 authTokensForAccount.remove(key);
6193 } else {
6194 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08006195 }
6196 }
6197
Amith Yamasani04e0d262012-02-14 11:50:53 -08006198 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
6199 String authTokenType) {
6200 synchronized (accounts.cacheLock) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006201 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08006202 if (authTokensForAccount == null) {
6203 // need to populate the cache for this account
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07006204 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006205 authTokensForAccount = AccountsDbUtils
6206 .findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006207 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006208 }
6209 return authTokensForAccount.get(authTokenType);
6210 }
6211 }
6212
Simranjit Kohli858511c2016-03-10 18:36:11 +00006213 protected String readUserDataInternalLocked(
6214 UserAccounts accounts, Account account, String key) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006215 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00006216 if (userDataForAccount == null) {
6217 // need to populate the cache for this account
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07006218 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006219 userDataForAccount = AccountsDbUtils.findUserExtrasForAccount(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00006220 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006221 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00006222 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08006223 }
6224
Kenny Guy07ad8dc2014-09-01 20:56:12 +01006225 private Context getContextForUser(UserHandle user) {
6226 try {
6227 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
6228 } catch (NameNotFoundException e) {
6229 // Default to mContext, not finding the package system is running as is unlikely.
6230 return mContext;
6231 }
6232 }
Sandra Kwan78812282015-11-04 11:19:47 -08006233
6234 private void sendResponse(IAccountManagerResponse response, Bundle result) {
6235 try {
6236 response.onResult(result);
6237 } catch (RemoteException e) {
6238 // if the caller is dead then there is no one to care about remote
6239 // exceptions
6240 if (Log.isLoggable(TAG, Log.VERBOSE)) {
6241 Log.v(TAG, "failure while notifying response", e);
6242 }
6243 }
6244 }
6245
6246 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
6247 String errorMessage) {
6248 try {
6249 response.onError(errorCode, errorMessage);
6250 } catch (RemoteException e) {
6251 // if the caller is dead then there is no one to care about remote
6252 // exceptions
6253 if (Log.isLoggable(TAG, Log.VERBOSE)) {
6254 Log.v(TAG, "failure while notifying response", e);
6255 }
6256 }
6257 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006258
6259 static class AccountsDbUtils {
6260
6261 static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
6262 String type) {
6263 Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
6264 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
6265 new String[]{name, type}, null, null, null);
6266 try {
6267 if (cursor.moveToNext()) {
6268 return cursor.getString(0);
6269 }
6270 return null;
6271 } finally {
6272 cursor.close();
6273 }
6274 }
6275
6276 static Map<Long, Account> findAllAccounts(SQLiteDatabase db) {
6277 LinkedHashMap<Long, Account> map = new LinkedHashMap<>();
6278 Cursor cursor = db.query(TABLE_ACCOUNTS,
6279 new String[] {ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
6280 null, null, null, null, ACCOUNTS_ID);
6281 try {
6282 while (cursor.moveToNext()) {
6283 final long accountId = cursor.getLong(0);
6284 final String accountType = cursor.getString(1);
6285 final String accountName = cursor.getString(2);
6286
6287 final Account account = new Account(accountName, accountType);
6288 map.put(accountId, account);
6289 }
6290 } finally {
6291 cursor.close();
6292 }
6293 return map;
6294 }
6295
6296 static String findAccountPreviousName(SQLiteDatabase db, Account account) {
6297 Cursor cursor = db.query(
6298 TABLE_ACCOUNTS,
6299 new String[]{ ACCOUNTS_PREVIOUS_NAME },
6300 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
6301 new String[] { account.name, account.type },
6302 null,
6303 null,
6304 null);
6305 try {
6306 if (cursor.moveToNext()) {
6307 return cursor.getString(0);
6308 }
6309 } finally {
6310 cursor.close();
6311 }
6312 return null;
6313 }
6314
6315 static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
6316 // Select accounts from CE that do not exist in DE
6317 Cursor cursor = db.rawQuery(
6318 "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
6319 + " FROM " + CE_TABLE_ACCOUNTS
6320 + " WHERE NOT EXISTS "
6321 + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS
6322 + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
6323 + " )", null);
6324 try {
6325 List<Account> accounts = new ArrayList<>(cursor.getCount());
6326 while (cursor.moveToNext()) {
6327 String accountName = cursor.getString(0);
6328 String accountType = cursor.getString(1);
6329 accounts.add(new Account(accountName, accountType));
6330 }
6331 return accounts;
6332 } finally {
6333 cursor.close();
6334 }
6335 }
6336
6337 static boolean deleteAccount(SQLiteDatabase db, long accountId) {
6338 return db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
6339 }
6340
6341 static boolean deleteCeAccount(SQLiteDatabase db, long accountId) {
6342 return db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
6343 }
6344
6345 /**
6346 * Returns information about auth tokens and their account for the specified query parameters.
6347 * Output is in the format:
6348 * <pre><code> | AUTHTOKEN_ID | ACCOUNT_NAME | AUTH_TOKEN_TYPE |</code></pre>
6349 */
6350 static Cursor findAuthtokenForAllAccounts(SQLiteDatabase db, String accountType,
6351 String authToken) {
6352 return db.rawQuery(
6353 "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
6354 + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
6355 + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
6356 + " FROM " + CE_TABLE_ACCOUNTS
6357 + " JOIN " + CE_TABLE_AUTHTOKENS
6358 + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
6359 + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
6360 + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN
6361 + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
6362 new String[]{authToken, accountType});
6363 }
6364
6365 static boolean deleteAuthtokensByAccountIdAndType(SQLiteDatabase db, long accountId,
6366 String authtokenType) {
6367 return db.delete(CE_TABLE_AUTHTOKENS,
6368 AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
6369 new String[] {String.valueOf(accountId), authtokenType}) > 0;
6370 }
6371
6372 static boolean deleteAuthToken(SQLiteDatabase db, String authTokenId) {
6373 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "= ?",
6374 new String[] {authTokenId}) > 0;
6375 }
6376
6377 static long insertAuthToken(SQLiteDatabase db, long accountId, String authTokenType,
6378 String authToken) {
6379 ContentValues values = new ContentValues();
6380 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
6381 values.put(AUTHTOKENS_TYPE, authTokenType);
6382 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
6383 return db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values);
6384 }
6385
6386 static Map<String, String> findAuthTokensByAccount(final SQLiteDatabase db, Account account) {
6387 HashMap<String, String> authTokensForAccount = new HashMap<>();
6388 Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
6389 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
6390 SELECTION_AUTHTOKENS_BY_ACCOUNT,
6391 new String[]{account.name, account.type},
6392 null, null, null);
6393 try {
6394 while (cursor.moveToNext()) {
6395 final String type = cursor.getString(0);
6396 final String authToken = cursor.getString(1);
6397 authTokensForAccount.put(type, authToken);
6398 }
6399 } finally {
6400 cursor.close();
6401 }
6402 return authTokensForAccount;
6403 }
6404
6405 static int updateAccountPassword(SQLiteDatabase db, long accountId, String password) {
6406 final ContentValues values = new ContentValues();
6407 values.put(ACCOUNTS_PASSWORD, password);
6408 return db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?",
6409 new String[] {String.valueOf(accountId)});
6410 }
6411
6412 static boolean deleteAuthTokensByAccountId(SQLiteDatabase db, long accountId) {
6413 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?",
6414 new String[] {String.valueOf(accountId)}) > 0;
6415 }
6416
6417 static long insertSharedAccount(SQLiteDatabase db, Account account) {
6418 ContentValues values = new ContentValues();
6419 values.put(ACCOUNTS_NAME, account.name);
6420 values.put(ACCOUNTS_TYPE, account.type);
6421 return db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
6422 }
6423
6424 static boolean deleteSharedAccount(SQLiteDatabase db, Account account) {
6425 return db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
6426 new String[] {account.name, account.type}) > 0;
6427 }
6428
6429 static int renameSharedAccount(SQLiteDatabase db, Account account, String newName) {
6430 final ContentValues values = new ContentValues();
6431 values.put(ACCOUNTS_NAME, newName);
6432 return db.update(TABLE_SHARED_ACCOUNTS,
6433 values,
6434 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
6435 new String[] { account.name, account.type });
6436 }
6437
6438 static List<Account> getSharedAccounts(SQLiteDatabase db) {
6439 ArrayList<Account> accountList = new ArrayList<>();
6440 Cursor cursor = null;
6441 try {
6442 cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE},
6443 null, null, null, null, null);
6444 if (cursor != null && cursor.moveToFirst()) {
6445 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
6446 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
6447 do {
6448 accountList.add(new Account(cursor.getString(nameIndex),
6449 cursor.getString(typeIndex)));
6450 } while (cursor.moveToNext());
6451 }
6452 } finally {
6453 if (cursor != null) {
6454 cursor.close();
6455 }
6456 }
6457 return accountList;
6458 }
6459
6460 static long findSharedAccountId(SQLiteDatabase db, Account account) {
6461 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
6462 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
6463 try {
6464 if (cursor.moveToNext()) {
6465 return cursor.getLong(0);
6466 }
6467 return -1;
6468 } finally {
6469 cursor.close();
6470 }
6471 }
6472
6473 static long findAccountId(SQLiteDatabase db, Account account) {
6474 Cursor cursor = db.query(
6475 TABLE_ACCOUNTS, new String[] {ACCOUNTS_ID},
6476 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
6477 try {
6478 if (cursor.moveToNext()) {
6479 return cursor.getLong(0);
6480 }
6481 return -1;
6482 } finally {
6483 cursor.close();
6484 }
6485 }
6486
6487 static long findExtrasIdByAccountId(SQLiteDatabase db, long accountId, String key) {
6488 Cursor cursor = db.query(
6489 CE_TABLE_EXTRAS, new String[] {EXTRAS_ID},
6490 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
6491 new String[]{key}, null, null, null);
6492 try {
6493 if (cursor.moveToNext()) {
6494 return cursor.getLong(0);
6495 }
6496 return -1;
6497 } finally {
6498 cursor.close();
6499 }
6500 }
6501
6502 static boolean updateExtra(SQLiteDatabase db, long extrasId, String value) {
6503 ContentValues values = new ContentValues();
6504 values.put(EXTRAS_VALUE, value);
6505 int rows = db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=?",
6506 new String[]{String.valueOf(extrasId)});
6507 return rows == 1;
6508 }
6509
6510 static long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
6511 ContentValues values = new ContentValues();
6512 values.put(EXTRAS_KEY, key);
6513 values.put(EXTRAS_ACCOUNTS_ID, accountId);
6514 values.put(EXTRAS_VALUE, value);
6515 return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
6516 }
6517
6518 static Map<String, String> findUserExtrasForAccount(SQLiteDatabase db, Account account) {
6519 Map<String, String> userExtrasForAccount = new HashMap<>();
6520 Cursor cursor = db.query(CE_TABLE_EXTRAS,
6521 COLUMNS_EXTRAS_KEY_AND_VALUE,
6522 SELECTION_USERDATA_BY_ACCOUNT,
6523 new String[] {account.name, account.type},
6524 null, null, null);
6525 try {
6526 while (cursor.moveToNext()) {
6527 final String tmpkey = cursor.getString(0);
6528 final String value = cursor.getString(1);
6529 userExtrasForAccount.put(tmpkey, value);
6530 }
6531 } finally {
6532 cursor.close();
6533 }
6534 return userExtrasForAccount;
6535 }
6536
6537 static long insertGrant(SQLiteDatabase db, long accountId, String authTokenType, int uid) {
6538 ContentValues values = new ContentValues();
6539 values.put(GRANTS_ACCOUNTS_ID, accountId);
6540 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
6541 values.put(GRANTS_GRANTEE_UID, uid);
6542 return db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
6543 }
6544
6545 static boolean deleteGrantsByUid(SQLiteDatabase db, int uid) {
6546 return db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
6547 new String[] {Integer.toString(uid)}) > 0;
6548 }
6549
6550 static boolean deleteGrantsByAccountIdAuthTokenTypeAndUid(SQLiteDatabase db, long accountId, String authTokenType, long uid) {
6551 return db.delete(TABLE_GRANTS,
6552 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
6553 + GRANTS_GRANTEE_UID + "=?",
6554 new String[] {String.valueOf(accountId), authTokenType, String.valueOf(uid)}) > 0;
6555 }
6556
6557 static List<Integer> findAllUidGrants(SQLiteDatabase db) {
6558 List<Integer> result = new ArrayList<>();
6559 final Cursor cursor = db.query(TABLE_GRANTS,
6560 new String[] {GRANTS_GRANTEE_UID},
6561 null, null, GRANTS_GRANTEE_UID, null, null);
6562 try {
6563 while (cursor.moveToNext()) {
6564 final int uid = cursor.getInt(0);
6565 result.add(uid);
6566 }
6567 } finally {
6568 cursor.close();
6569 }
6570 return result;
6571 }
6572
6573 static long findMatchingGrantsCount(SQLiteDatabase db,
6574 int uid, String authTokenType, Account account) {
6575 String[] args = { String.valueOf(uid), authTokenType,
6576 account.name, account.type};
6577 return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args);
6578 }
6579
6580 static long insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid) {
6581 ContentValues values = new ContentValues();
6582 values.put(META_KEY,
6583 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
6584 values.put(META_VALUE, uid);
6585 return db.insert(TABLE_META, null, values);
6586 }
6587
6588 static long insertOrReplaceMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType,
6589 int uid) {
6590 ContentValues values = new ContentValues();
6591 values.put(META_KEY,
6592 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
6593 values.put(META_VALUE, uid);
6594 return db.insertWithOnConflict(TABLE_META, null, values,
6595 SQLiteDatabase.CONFLICT_REPLACE);
6596 }
6597
6598
6599 static Map<String, Integer> findMetaAuthUid(SQLiteDatabase db) {
6600 Cursor metaCursor = db.query(
6601 TABLE_META,
6602 new String[] {META_KEY, META_VALUE},
6603 SELECTION_META_BY_AUTHENTICATOR_TYPE,
6604 new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
6605 null /* groupBy */,
6606 null /* having */,
6607 META_KEY);
6608 Map<String, Integer> map = new LinkedHashMap<>();
6609 try {
6610 while (metaCursor.moveToNext()) {
6611 String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
6612 String uidStr = metaCursor.getString(1);
6613 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uidStr)) {
6614 // Should never happen.
6615 Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
6616 + ", uid empty: " + TextUtils.isEmpty(uidStr));
6617 continue;
6618 }
6619 int uid = Integer.parseInt(metaCursor.getString(1));
6620 map.put(type, uid);
6621 }
6622 } finally {
6623 metaCursor.close();
6624 }
6625 return map;
6626 }
6627
6628 static boolean deleteMetaByAuthTypeAndUid(SQLiteDatabase db, String type, int uid) {
6629 return db.delete(
6630 TABLE_META,
6631 META_KEY + "=? AND " + META_VALUE + "=?",
6632 new String[] {
6633 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
6634 String.valueOf(uid)}
6635 ) > 0;
6636 }
6637
6638 static void dumpAccountsTable(SQLiteDatabase db, PrintWriter pw) {
6639 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
6640 null, null, ACCOUNTS_TYPE, null, null);
6641 try {
6642 while (cursor.moveToNext()) {
6643 // print type,count
6644 pw.println(cursor.getString(0) + "," + cursor.getString(1));
6645 }
6646 } finally {
6647 if (cursor != null) {
6648 cursor.close();
6649 }
6650 }
6651 }
6652
6653 static void dumpDebugTable(SQLiteDatabase db, PrintWriter pw) {
6654 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
6655 null, null, null, null, DebugDbHelper.TIMESTAMP);
6656 pw.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
6657 pw.println("Accounts History");
6658 try {
6659 while (cursor.moveToNext()) {
6660 // print type,count
6661 pw.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
6662 cursor.getString(2) + "," + cursor.getString(3) + ","
6663 + cursor.getString(4) + "," + cursor.getString(5));
6664 }
6665 } finally {
6666 cursor.close();
6667 }
6668 }
Svet Ganov890a2102016-08-24 00:08:00 -07006669 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006670
Svet Ganovf6d424f12016-09-20 20:18:53 -07006671 private final class AccountAccessTracker extends IAccountAccessTracker.Stub {
6672 @Override
6673 public void onAccountAccessed() throws RemoteException {
6674 final int uid = Binder.getCallingUid();
6675 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
6676 return;
6677 }
6678 final int userId = UserHandle.getCallingUserId();
6679 final long identity = Binder.clearCallingIdentity();
6680 try {
6681 for (Account account : getAccounts(userId, mContext.getOpPackageName())) {
6682 IAccountAccessTracker accountTracker = account.getAccessTracker();
6683 if (accountTracker != null && asBinder() == accountTracker.asBinder()) {
6684 // An app just accessed the account. At this point it knows about
6685 // it and there is not need to hide this account from the app.
6686 if (!hasAccountAccess(account, null, uid)) {
6687 updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
6688 uid, true);
6689 }
6690 }
6691 }
6692 } finally {
6693 Binder.restoreCallingIdentity(identity);
6694 }
6695 }
6696 }
6697
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006698 private final class AccountManagerInternalImpl extends AccountManagerInternal {
6699 @Override
6700 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
6701 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
6702 if (account == null) {
6703 Slog.w(TAG, "account cannot be null");
6704 return;
6705 }
6706 if (packageName == null) {
6707 Slog.w(TAG, "packageName cannot be null");
6708 return;
6709 }
6710 if (userId < UserHandle.USER_SYSTEM) {
6711 Slog.w(TAG, "user id must be concrete");
6712 return;
6713 }
6714 if (callback == null) {
6715 Slog.w(TAG, "callback cannot be null");
6716 return;
6717 }
6718
Svet Ganovf6d424f12016-09-20 20:18:53 -07006719 if (AccountManagerService.this.hasAccountAccess(account, packageName,
6720 new UserHandle(userId))) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006721 Bundle result = new Bundle();
6722 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
6723 callback.sendResult(result);
6724 return;
6725 }
6726
6727 final int uid;
6728 try {
6729 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
6730 } catch (NameNotFoundException e) {
6731 Slog.e(TAG, "Unknown package " + packageName);
6732 return;
6733 }
6734
6735 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
Svet Ganovf6d424f12016-09-20 20:18:53 -07006736 final UserAccounts userAccounts;
6737 synchronized (mUsers) {
6738 userAccounts = mUsers.get(userId);
6739 }
6740 doNotification(userAccounts, account, null, intent, packageName, userId);
6741 }
6742
6743 @Override
6744 public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) {
6745 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
6746 mAppPermissionChangeListeners.add(listener);
6747 }
6748
6749 @Override
6750 public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) {
6751 return AccountManagerService.this.hasAccountAccess(account, null, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006752 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006753 }
Fred Quintana60307342009-03-24 22:48:12 -07006754}