blob: 06ea0daf49dea3cf2b234a0bb1ad8cc77312a0b4 [file] [log] [blame]
Fred Quintana60307342009-03-24 22:48:12 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.accounts;
Fred Quintana60307342009-03-24 22:48:12 -070018
Doug Zongker885cfc232009-10-21 16:52:44 -070019import android.Manifest;
Carlos Valdivia91979be2015-05-22 14:11:35 -070020import android.accounts.AbstractAccountAuthenticator;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080021import android.accounts.Account;
22import android.accounts.AccountAndUser;
23import android.accounts.AccountAuthenticatorResponse;
24import android.accounts.AccountManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070025import android.accounts.AccountManagerInternal;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080026import android.accounts.AuthenticatorDescription;
Amith Yamasani23c8b962013-04-10 13:37:18 -070027import android.accounts.CantAddAccountActivity;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080028import android.accounts.GrantCredentialsPermissionActivity;
29import android.accounts.IAccountAuthenticator;
30import android.accounts.IAccountAuthenticatorResponse;
31import android.accounts.IAccountManager;
32import android.accounts.IAccountManagerResponse;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070033import android.annotation.IntRange;
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -070034import android.annotation.NonNull;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080035import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070036import android.app.ActivityManagerNative;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070037import android.app.ActivityThread;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070038import android.app.AppGlobals;
Svetoslavf3f02ac2015-09-08 14:36:35 -070039import android.app.AppOpsManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070040import android.app.INotificationManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070041import android.app.Notification;
42import android.app.NotificationManager;
43import android.app.PendingIntent;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000044import android.app.admin.DeviceAdminInfo;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010045import android.app.admin.DevicePolicyManager;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000046import android.app.admin.DevicePolicyManagerInternal;
Fred Quintanaa698f422009-04-08 19:14:54 -070047import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070048import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070049import android.content.ContentValues;
50import android.content.Context;
51import android.content.Intent;
52import android.content.IntentFilter;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070053import android.content.IntentSender;
Fred Quintanab839afc2009-10-14 15:57:28 -070054import android.content.ServiceConnection;
Carlos Valdivia6ede9c32016-03-10 20:12:32 -080055import android.content.pm.ActivityInfo;
Doug Zongker885cfc232009-10-21 16:52:44 -070056import android.content.pm.ApplicationInfo;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070057import android.content.pm.IPackageManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070058import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070059import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070060import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070061import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070062import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070063import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070064import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070065import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070066import android.database.Cursor;
67import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070068import android.database.sqlite.SQLiteDatabase;
69import android.database.sqlite.SQLiteOpenHelper;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070070import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070071import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070072import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080073import android.os.Environment;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070074import android.os.FileUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070075import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070076import android.os.IBinder;
77import android.os.Looper;
78import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070079import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070080import android.os.Process;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070081import android.os.RemoteCallback;
Fred Quintanaa698f422009-04-08 19:14:54 -070082import android.os.RemoteException;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070083import android.os.ServiceManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070084import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070085import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070086import android.os.UserManager;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070087import android.os.storage.StorageManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070088import android.text.TextUtils;
89import android.util.Log;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070090import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070091import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080092import android.util.SparseArray;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070093import android.util.SparseBooleanArray;
Fred Quintana60307342009-03-24 22:48:12 -070094
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070095import com.android.internal.R;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -070096import com.android.internal.annotations.VisibleForTesting;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070097import com.android.internal.content.PackageMonitor;
Amith Yamasani67df64b2012-12-14 12:09:36 -080098import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -080099import com.android.internal.util.IndentingPrintWriter;
Fyodor Kupolov35f68082016-04-06 12:14:17 -0700100import com.android.internal.util.Preconditions;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +0000101import com.android.server.LocalServices;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700102import com.android.server.ServiceThread;
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600103import com.android.server.SystemService;
104
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700105import com.google.android.collect.Lists;
106import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700107
Oscar Montemayora8529f62009-11-18 10:14:20 -0800108import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -0700109import java.io.FileDescriptor;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700110import java.io.IOException;
Fred Quintanaa698f422009-04-08 19:14:54 -0700111import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -0800112import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -0700113import java.security.MessageDigest;
114import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700115import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -0700116import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -0800117import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700118import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700119import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700120import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700121import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -0800122import java.util.LinkedHashMap;
Tejas Khorana7b88f0e2016-06-13 13:06:35 -0700123import java.util.LinkedList;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700124import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800125import java.util.Map;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800126import java.util.Map.Entry;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700127import java.util.Set;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700128import java.util.concurrent.atomic.AtomicInteger;
129import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700130
Fred Quintana60307342009-03-24 22:48:12 -0700131/**
132 * A system service that provides account, password, and authtoken management for all
133 * accounts on the device. Some of these calls are implemented with the help of the corresponding
134 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
135 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700136 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700137 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700138 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700139public class AccountManagerService
140 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800141 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -0700142 private static final String TAG = "AccountManagerService";
143
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600144 public static class Lifecycle extends SystemService {
145 private AccountManagerService mService;
146
147 public Lifecycle(Context context) {
148 super(context);
149 }
150
151 @Override
152 public void onStart() {
153 mService = new AccountManagerService(getContext());
154 publishBinderService(Context.ACCOUNT_SERVICE, mService);
155 }
156
157 @Override
158 public void onBootPhase(int phase) {
159 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
160 mService.systemReady();
161 }
162 }
163
164 @Override
165 public void onUnlockUser(int userHandle) {
166 mService.onUnlockUser(userHandle);
167 }
168 }
169
Fred Quintana60307342009-03-24 22:48:12 -0700170 private static final String DATABASE_NAME = "accounts.db";
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700171 private static final int PRE_N_DATABASE_VERSION = 9;
172 private static final int CE_DATABASE_VERSION = 10;
173 private static final int DE_DATABASE_VERSION = 1;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700174
175 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700176
177 private final Context mContext;
178
Fred Quintana56285a62010-12-02 14:20:51 -0800179 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700180 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700181 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -0800182
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700183 private final MessageHandler mHandler;
Tejas Khorana7b88f0e2016-06-13 13:06:35 -0700184
Fred Quintana60307342009-03-24 22:48:12 -0700185 // Messages that can be sent on mHandler
186 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700187 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700188
Fred Quintana56285a62010-12-02 14:20:51 -0800189 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700190
191 private static final String TABLE_ACCOUNTS = "accounts";
192 private static final String ACCOUNTS_ID = "_id";
193 private static final String ACCOUNTS_NAME = "name";
194 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700195 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700196 private static final String ACCOUNTS_PASSWORD = "password";
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700197 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800198 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
199 "last_password_entry_time_millis_epoch";
Fred Quintana60307342009-03-24 22:48:12 -0700200
201 private static final String TABLE_AUTHTOKENS = "authtokens";
202 private static final String AUTHTOKENS_ID = "_id";
203 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
204 private static final String AUTHTOKENS_TYPE = "type";
205 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
206
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700207 private static final String TABLE_GRANTS = "grants";
208 private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
209 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
210 private static final String GRANTS_GRANTEE_UID = "uid";
211
Fred Quintana60307342009-03-24 22:48:12 -0700212 private static final String TABLE_EXTRAS = "extras";
213 private static final String EXTRAS_ID = "_id";
214 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
215 private static final String EXTRAS_KEY = "key";
216 private static final String EXTRAS_VALUE = "value";
217
218 private static final String TABLE_META = "meta";
219 private static final String META_KEY = "key";
220 private static final String META_VALUE = "value";
221
Amith Yamasani67df64b2012-12-14 12:09:36 -0800222 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700223 private static final String SHARED_ACCOUNTS_ID = "_id";
224
225 private static final String PRE_N_DATABASE_NAME = "accounts.db";
226 private static final String CE_DATABASE_NAME = "accounts_ce.db";
227 private static final String DE_DATABASE_NAME = "accounts_de.db";
228 private static final String CE_DB_PREFIX = "ceDb.";
229 private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS;
230 private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
231 private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800232
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700233 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
234 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700235 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800236
Carlos Valdivia91979be2015-05-22 14:11:35 -0700237 static {
238 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
239 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
240 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700241
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700242 private static final String COUNT_OF_MATCHING_GRANTS = ""
243 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
244 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
245 + " AND " + GRANTS_GRANTEE_UID + "=?"
246 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
247 + " AND " + ACCOUNTS_NAME + "=?"
248 + " AND " + ACCOUNTS_TYPE + "=?";
249
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700250 private static final String COUNT_OF_MATCHING_GRANTS_ANY_TOKEN = ""
251 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
252 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
253 + " AND " + GRANTS_GRANTEE_UID + "=?"
254 + " AND " + ACCOUNTS_NAME + "=?"
255 + " AND " + ACCOUNTS_TYPE + "=?";
256
Fred Quintana56285a62010-12-02 14:20:51 -0800257 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
258 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
Carlos Valdivia91979be2015-05-22 14:11:35 -0700259
Fred Quintana56285a62010-12-02 14:20:51 -0800260 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
261 AUTHTOKENS_AUTHTOKEN};
262
263 private static final String SELECTION_USERDATA_BY_ACCOUNT =
264 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
265 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
266
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800267 private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX =
268 "auth_uid_for_type:";
269 private static final String META_KEY_DELIMITER = ":";
270 private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
271
Fred Quintanaa698f422009-04-08 19:14:54 -0700272 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700273 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
274
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700275 private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
276
Amith Yamasani04e0d262012-02-14 11:50:53 -0800277 static class UserAccounts {
278 private final int userId;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700279 private final DeDatabaseHelper openHelper;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800280 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
281 credentialsPermissionNotificationIds =
282 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
283 private final HashMap<Account, Integer> signinRequiredNotificationIds =
284 new HashMap<Account, Integer>();
285 private final Object cacheLock = new Object();
286 /** protected by the {@link #cacheLock} */
Amith Yamasanib483a992012-05-22 13:14:25 -0700287 private final HashMap<String, Account[]> accountCache =
288 new LinkedHashMap<String, Account[]>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800289 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700290 private final Map<Account, Map<String, String>> userDataCache = new HashMap<>();
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>> authTokenCache = new HashMap<>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700293
294 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700295 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700296
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700297 /** protected by the {@link #cacheLock} */
298 private final Map<String, ArrayList<Integer>> mApplicationAccountRequestMappings =
299 new HashMap<>();
300
301 /* Together the below two Sparse Arrays serve as visible list. One maps UID to account
302 number. Another maps Account number to Account.*/
303
304 /** protected by the {@link #cacheLock} */
305 private final SparseArray<ArrayList<Integer>> mVisibleListUidToMockAccountNumbers =
306 new SparseArray<>();
307
308 //TODO: Instead of using Mock Account IDs, use the actual account IDs.
309 /** protected by the {@link #cacheLock} */
310 private final SparseArray<Account> mMockAccountIdToAccount = new SparseArray<>();
311
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700312 /**
313 * protected by the {@link #cacheLock}
314 *
315 * Caches the previous names associated with an account. Previous names
316 * should be cached because we expect that when an Account is renamed,
317 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
318 * want to know if the accounts they care about have been renamed.
319 *
320 * The previous names are wrapped in an {@link AtomicReference} so that
321 * we can distinguish between those accounts with no previous names and
322 * those whose previous names haven't been cached (yet).
323 */
324 private final HashMap<Account, AtomicReference<String>> previousNameCache =
325 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800326
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700327 private int debugDbInsertionPoint = -1;
328 private SQLiteStatement statementForLogging;
329
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700330 UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800331 this.userId = userId;
332 synchronized (cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700333 openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800334 }
335 }
336 }
337
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700338 private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600339 private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800340
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700341 private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
Fred Quintana31957f12009-10-21 13:43:10 -0700342 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700343
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700344 /**
345 * This should only be called by system code. One should only call this after the service
346 * has started.
347 * @return a reference to the AccountManagerService instance
348 * @hide
349 */
350 public static AccountManagerService getSingleton() {
351 return sThis.get();
352 }
Fred Quintana60307342009-03-24 22:48:12 -0700353
Fred Quintana56285a62010-12-02 14:20:51 -0800354 public AccountManagerService(Context context) {
355 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700356 }
357
Fred Quintana56285a62010-12-02 14:20:51 -0800358 public AccountManagerService(Context context, PackageManager packageManager,
359 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700360 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800361 mPackageManager = packageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700362 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fred Quintana60307342009-03-24 22:48:12 -0700363
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700364 ServiceThread serviceThread = new ServiceThread(TAG,
365 android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
366 serviceThread.start();
367 mHandler = new MessageHandler(serviceThread.getLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700368
Fred Quintana56285a62010-12-02 14:20:51 -0800369 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800370 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700371
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700372 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800373
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700374 addRequestsForPreInstalledApplications();
375
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800376 IntentFilter intentFilter = new IntentFilter();
377 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
378 intentFilter.addDataScheme("package");
379 mContext.registerReceiver(new BroadcastReceiver() {
380 @Override
381 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700382 // Don't delete accounts when updating a authenticator's
383 // package.
384 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700385 /* Purging data requires file io, don't block the main thread. This is probably
386 * less than ideal because we are introducing a race condition where old grants
387 * could be exercised until they are purged. But that race condition existed
388 * anyway with the broadcast receiver.
389 *
390 * Ideally, we would completely clear the cache, purge data from the database,
391 * and then rebuild the cache. All under the cache lock. But that change is too
392 * large at this point.
393 */
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700394 Runnable purgingRunnable = new Runnable() {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700395 @Override
396 public void run() {
397 purgeOldGrantsAll();
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700398
399 /* clears application request's for account types supported */
400 int uidOfUninstalledApplication =
401 intent.getIntExtra(Intent.EXTRA_UID, -1);
402 if(uidOfUninstalledApplication != -1) {
403 clearRequestedAccountVisibility(uidOfUninstalledApplication,
404 getUserAccounts(UserHandle.getUserId(
405 uidOfUninstalledApplication)));
406 }
407
408 /* removes visibility of previous UID of this uninstalled application*/
409 removeAccountVisibilityAllAccounts(uidOfUninstalledApplication,
410 getUserAccounts(UserHandle.getUserId(
411 uidOfUninstalledApplication)));
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700412 }
413 };
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700414 mHandler.post(purgingRunnable);
Carlos Valdivia23f58262014-09-05 10:52:41 -0700415 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700416
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800417 }
418 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800419
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700420 IntentFilter packageAddedOrChangedFilter = new IntentFilter();
421 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
422 packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
423 packageAddedOrChangedFilter.addDataScheme("package");
424 mContext.registerReceiverAsUser(new BroadcastReceiver() {
425 @Override
426 public void onReceive(Context context1, Intent intent) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700427 mHandler.post(new Runnable() {
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700428 @Override
429 public void run() {
430 int uidOfInstalledApplication =
431 intent.getIntExtra(Intent.EXTRA_UID, -1);
432 if(uidOfInstalledApplication != -1) {
433 registerAccountTypesSupported(
434 uidOfInstalledApplication,
435 getUserAccounts(
436 UserHandle.getUserId(uidOfInstalledApplication)));
437 }
438 }
439 });
440 }
441 }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
442
Amith Yamasani13593602012-03-22 16:16:17 -0700443 IntentFilter userFilter = new IntentFilter();
444 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800445 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700446 @Override
447 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800448 String action = intent.getAction();
449 if (Intent.ACTION_USER_REMOVED.equals(action)) {
450 onUserRemoved(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800451 }
Amith Yamasani13593602012-03-22 16:16:17 -0700452 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800453 }, UserHandle.ALL, userFilter, null, null);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700454
455 LocalServices.addService(AccountManagerInternal.class, new AccountManagerInternalImpl());
456
457 // Need to cancel account request notifications if the update/install can access the account
458 new PackageMonitor() {
459 @Override
460 public void onPackageAdded(String packageName, int uid) {
461 // Called on a handler, and running as the system
462 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
463 }
464
465 @Override
466 public void onPackageUpdateFinished(String packageName, int uid) {
467 // Called on a handler, and running as the system
468 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
469 }
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700470 }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700471
472 // Cancel account request notification if an app op was preventing the account access
473 mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
474 new AppOpsManager.OnOpChangedInternalListener() {
475 @Override
476 public void onOpChanged(int op, String packageName) {
477 try {
478 final int userId = ActivityManager.getCurrentUser();
479 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
480 final int mode = mAppOpsManager.checkOpNoThrow(
481 AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
482 if (mode == AppOpsManager.MODE_ALLOWED) {
483 final long identity = Binder.clearCallingIdentity();
484 try {
485 cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
486 } finally {
487 Binder.restoreCallingIdentity(identity);
488 }
489 }
490 } catch (NameNotFoundException e) {
491 /* ignore */
492 }
493 }
494 });
495
496 // Cancel account request notification if a permission was preventing the account access
497 mPackageManager.addOnPermissionsChangeListener(
498 (int uid) -> {
499 Account[] accounts = null;
500 String[] packageNames = mPackageManager.getPackagesForUid(uid);
501 if (packageNames != null) {
502 final int userId = UserHandle.getUserId(uid);
503 final long identity = Binder.clearCallingIdentity();
504 try {
505 for (String packageName : packageNames) {
506 if (mContext.getPackageManager().checkPermission(
507 Manifest.permission.GET_ACCOUNTS, packageName)
508 != PackageManager.PERMISSION_GRANTED) {
509 continue;
510 }
511
512 if (accounts == null) {
513 accounts = getAccountsAsUser(null, userId, "android");
514 if (ArrayUtils.isEmpty(accounts)) {
515 return;
516 }
517 }
518
519 for (Account account : accounts) {
520 cancelAccountAccessRequestNotificationIfNeeded(
521 account, uid, packageName, true);
522 }
523 }
524 } finally {
525 Binder.restoreCallingIdentity(identity);
526 }
527 }
528 });
529 }
530
531 private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
532 boolean checkAccess) {
533 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
534 for (Account account : accounts) {
535 cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
536 }
537 }
538
539 private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
540 boolean checkAccess) {
541 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
542 for (Account account : accounts) {
543 cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
544 }
545 }
546
547 private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
548 boolean checkAccess) {
549 String[] packageNames = mPackageManager.getPackagesForUid(uid);
550 if (packageNames != null) {
551 for (String packageName : packageNames) {
552 cancelAccountAccessRequestNotificationIfNeeded(account, uid,
553 packageName, checkAccess);
554 }
555 }
556 }
557
558 private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
559 int uid, String packageName, boolean checkAccess) {
560 if (!checkAccess || hasAccountAccess(account, packageName,
561 UserHandle.getUserHandleForUid(uid))) {
562 cancelNotification(getCredentialPermissionNotificationId(account,
563 AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName,
564 UserHandle.getUserHandleForUid(uid));
565 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800566 }
567
Dianne Hackborn164371f2013-10-01 19:10:13 -0700568 @Override
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700569 public boolean addAccountExplicitlyWithUid(Account account, String password, Bundle extras,
570 int[] selectedUids) {
571 if(addAccountExplicitly(account,password,extras)) {
572 for(int thisUid : selectedUids) {
573 makeAccountVisible(account, thisUid);
574 }
575 return true;
576 }
577 return false;
578 }
579
580 @Override
581 public int[] getRequestingUidsForType(String accountType) {
582 int callingUid = Binder.getCallingUid();
583 if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
584 String msg = String.format(
585 "uid %s cannot get secrets for accounts of type: %s",
586 callingUid,
587 accountType);
588 throw new SecurityException(msg);
589 }
590 return getRequestingUidsForType(accountType, getUserAccounts(
591 UserHandle.getUserId(callingUid)));
592 }
593
594 /**
595 * Returns all UIDs for applications that requested the account type. This method
596 * is called indirectly by the Authenticator and AccountManager
597 *
598 * @param accountType authenticator would like to know the requesting apps of
599 * @param ua UserAccount that currently hosts the account and application
600 *
601 * @return ArrayList of all UIDs that support accounts of this
602 * account type that seek approval (to be used to know which accounts for
603 * the authenticator to include in addAccountExplicitly). Null if none.
604 */
605 private int[] getRequestingUidsForType(String accountType, UserAccounts ua) {
606 synchronized(ua.cacheLock) {
607 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
608 ua.mApplicationAccountRequestMappings;
609 ArrayList<Integer> allUidsForAccountType = userApplicationAccountRequestMappings.get(
610 accountType);
611 if(allUidsForAccountType == null) {
612 return null;
613 }
614 int[] toReturn = new int[allUidsForAccountType.size()];
615 for(int i = 0 ; i < toReturn.length ; i++) {
616 toReturn[i] = allUidsForAccountType.get(i);
617 }
618 return toReturn;
619 }
620 }
621
622 @Override
623 public boolean isAccountVisible(Account a, int uid) {
624 int callingUid = Binder.getCallingUid();
625 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
626 String msg = String.format(
627 "uid %s cannot get secrets for accounts of type: %s",
628 callingUid,
629 a.type);
630 throw new SecurityException(msg);
631 }
632 return isAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
633 }
634
635 /**
636 * Checks visibility of certain account of a process identified
637 * by a given UID. This is called by the Authenticator indirectly.
638 *
639 * @param a The account to check visibility of
640 * @param uid UID to check visibility of
641 * @param ua UserAccount that currently hosts the account and application
642 *
643 * @return True if application has access to the account
644 *
645 */
646 private boolean isAccountVisible(Account a, int uid, UserAccounts ua) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700647 if(isAccountManagedByCaller(a.type, uid, UserHandle.getUserId(uid))) {
648 return true;
649 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700650 int accountMapping = getMockAccountNumber(a, ua);
651 if(accountMapping < 0) {
652 return true;
653 }
654 synchronized(ua.cacheLock) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700655 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700656 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
657 ua.mVisibleListUidToMockAccountNumbers;
658 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
Tejas Khorana69990d92016-08-03 11:19:40 -0700659 int indexOfAccountMapping = userAcctIdToAcctMap.indexOfValueByValue(a);
660 return indexOfAccountMapping == -1 || (linkedAccountsToUid != null
661 && linkedAccountsToUid.contains(accountMapping));
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700662 }
663 }
664
665 @Override
666 public boolean makeAccountVisible(Account a, int uid) {
667 int callingUid = Binder.getCallingUid();
668 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
669 String msg = String.format(
670 "uid %s cannot get secrets for accounts of type: %s",
671 callingUid,
672 a.type);
673 throw new SecurityException(msg);
674 }
675 return makeAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
676 }
677
678 /**
679 * Gives a certain UID, represented a application, access to an account. This method
680 * is called indirectly by the Authenticator.
681 *
682 * @param a Account to make visible
683 * @param uid to add visibility of the Account from
684 * @param ua UserAccount that currently hosts the account and application
685 *
686 * @return True if account made visible to application and was not previously visible.
687 */
688 private boolean makeAccountVisible(Account a, int uid, UserAccounts ua) {
689 int accountMapping = getMockAccountNumber(a, ua);
690 if(accountMapping < 0) {
691 accountMapping = makeAccountNumber(a, ua);
692 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700693 synchronized(ua.cacheLock) {
694 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
695 ua.mVisibleListUidToMockAccountNumbers;
696 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
697 if(linkedAccountsToUid == null) {
698 linkedAccountsToUid = new ArrayList<>();
699 linkedAccountsToUid.add(accountMapping);
700 userWlUidToMockAccountNums.put(uid, linkedAccountsToUid);
701 } else if(!linkedAccountsToUid.contains(accountMapping)) {
702 linkedAccountsToUid.add(accountMapping);
703 } else {
704 return false;
705 }
706 }
707
708 String[] subPackages = mPackageManager.getPackagesForUid(uid);
709 if(subPackages != null) {
710 for(String subPackage : subPackages) {
711 sendNotification(subPackage, a);
712 }
713 }
714 return true;
715 }
716
717 @Override
718 public boolean removeAccountVisibility(Account a, int uid) {
719 int callingUid = Binder.getCallingUid();
720 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
721 String msg = String.format(
722 "uid %s cannot get secrets for accounts of type: %s",
723 callingUid,
724 a.type);
725 throw new SecurityException(msg);
726 }
727 return removeAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
728 }
729
730 /**
731 * Removes visibility of certain account of a process identified
732 * by a given UID to an application. This is called directly by the
733 * AccountManager and indirectly by the Authenticator.
734 *
735 * @param a Account to remove visibility from
736 * @param uid UID to remove visibility of the Account from
737 * @param ua UserAccount that hosts the account and application
738 *
739 * @return True if application access to account removed and was previously visible.
740 */
741 private boolean removeAccountVisibility(Account a, int uid, UserAccounts ua) {
742 int accountMapping = getMockAccountNumber(a, ua);
743 if(accountMapping < 0) {
744 return false;
745 }
746 synchronized(ua.cacheLock) {
747 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
748 ua.mVisibleListUidToMockAccountNumbers;
749 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
750 if(linkedAccountsToUid != null) {
751 boolean toReturn = linkedAccountsToUid.remove((Integer) accountMapping);
752 if(linkedAccountsToUid.size() == 0) {
753 userWlUidToMockAccountNums.remove(uid);
754 }
755 return toReturn;
756 }
757 }
758 return false;
759 }
760
761 /**
762 * Registers an application's preferences for supported account types for login. This is
763 * a helper method of requestAccountVisibility and indirectly called by AccountManager.
764 *
765 * @param accountTypes account types third party app is willing to support
766 * @param uid of application requesting account visibility
767 * @param ua UserAccount that hosts the account and application
768 */
769 private void addRequestedAccountsVisibility(String[] accountTypes, int uid, UserAccounts ua) {
770 synchronized(ua.cacheLock) {
771 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
772 ua.mApplicationAccountRequestMappings;
773 for(String accountType : accountTypes) {
774 ArrayList<Integer> accountUidAppList = userApplicationAccountRequestMappings
775 .get(accountType);
776 if(accountUidAppList == null) {
777 accountUidAppList = new ArrayList<>();
778 accountUidAppList.add(uid);
779 userApplicationAccountRequestMappings.put(accountType, accountUidAppList);
780 } else if (!accountUidAppList.contains(uid)) {
781 accountUidAppList.add(uid);
782 }
783 }
784 }
785 }
786
787 /**
788 * Registers the requested login account types requested by all the applications already
789 * installed on the device.
790 */
791 private void addRequestsForPreInstalledApplications() {
792 List<PackageInfo> allInstalledPackages = mContext.getPackageManager().
793 getInstalledPackages(0);
794 for(PackageInfo pi : allInstalledPackages) {
795 int currentUid = pi.applicationInfo.uid;
796 if(currentUid != -1) {
797 registerAccountTypesSupported(currentUid,
798 getUserAccounts(UserHandle.getUserId(currentUid)));
799 }
800 }
801 }
802
803 /**
804 * Clears all preferences an application had for login account types it offered
805 * support for. This method is used by AccountManager after application is
806 * uninstalled.
807 *
808 * @param uid Uid of the application to clear account type preferences
809 * @param ua UserAccount that hosted the account and application
810 *
811 * @return true if any previous settings were overridden.
812 */
813 private boolean clearRequestedAccountVisibility(int uid, UserAccounts ua) {
814 boolean accountsDeleted = false;
815 ArrayList<String> accountTypesToRemove = new ArrayList<>();
816 synchronized(ua.cacheLock) {
817 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
818 ua.mApplicationAccountRequestMappings;
819 Set<Entry<String, ArrayList<Integer>>> accountTypeAppListEntries =
820 userApplicationAccountRequestMappings.entrySet();
821
822 for(Entry<String, ArrayList<Integer>> entry : accountTypeAppListEntries) {
823 ArrayList<Integer> supportedApps = entry.getValue();
824 if(supportedApps.remove((Integer) uid)) {
825 accountsDeleted = true;
826 }
827
828 if(supportedApps.isEmpty()) {
829 accountTypesToRemove.add(entry.getKey());
830 }
831 }
832
833 for(String s : accountTypesToRemove) {
834 userApplicationAccountRequestMappings.remove(s);
835 }
836 }
837
838 return accountsDeleted;
839 }
840
841 /**
842 * Retrieves the mock account number associated with an Account in order to later retrieve
843 * the account from the Integer-Account Mapping. An account number is not the same as
844 * accountId in the database. This method can be indirectly called by AccountManager and
845 * indirectly by the Authenticator.
846 *
847 * @param a account to retrieve account number mapping
848 * @param ua UserAccount that currently hosts the account and application
849 *
850 * @return account number affiliated with the Account in question. Negative number if none.
851 */
852 private int getMockAccountNumber(Account a, UserAccounts ua) {
853 //TODO: Each account is linked to AccountId rather than generated mock account numbers
854 SparseArray<Account> userAcctIdToAcctMap =
855 ua.mMockAccountIdToAccount;
856 synchronized(ua.cacheLock) {
857 int indexOfAccount = userAcctIdToAcctMap.indexOfValueByValue(a);
858 if(indexOfAccount < 0) {
859 return -1;
860 }
861 return userAcctIdToAcctMap.keyAt(indexOfAccount);
862 }
863 }
864
865 /**
866 * Returns a full list of accounts that a certain UID is allowed access
867 * based on the visible list entries.
868 *
869 * @param uid of application to retrieve visible listed accounts for
870 * @param ua UserAccount that currently hosts the account and application
871 *
872 * @return array of Account values that are accessible by the given uids
873 */
874 private Account[] getVisibleListedAccounts(int uid, UserAccounts ua) {
875 ArrayList<Account> visibleListedAccounts = new ArrayList<>();
876 synchronized(ua.cacheLock) {
877 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
878 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
879 ua.mVisibleListUidToMockAccountNumbers;
880 ArrayList<Integer> visibleListedUidAccountNumbers =
881 userWlUidToMockAccountNums.get(uid);
882 if(visibleListedUidAccountNumbers != null) {
883 for(Integer accountNumber : visibleListedUidAccountNumbers) {
884 Account currentAccount = userAcctIdToAcctMap.get(accountNumber);
885 visibleListedAccounts.add(currentAccount);
886 }
887 }
888 }
889 Account[] arrVisibleListedAccounts = new Account[visibleListedAccounts.size()];
890 return visibleListedAccounts.toArray(arrVisibleListedAccounts);
891 }
892
893 /**
894 * Makes an account number for a given Account to be mapped to.
895 * This method is called by makeVisible if an Account does not have
896 * a mapping for the visible list. This method is thus indirectly
897 * called by the Authenticator.
898 *
899 * @param a account to make an account number mapping of
900 * @param ua UserAccount that currently hosts the account and application
901 *
902 * @return account number created to map to the given account
903 */
904 // TODO: Remove this method and use accountId from DB.
905 private int makeAccountNumber(Account a, UserAccounts ua) {
906 synchronized(ua.cacheLock) {
907 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
908 int newAccountMapping = 0;
909 while(userAcctIdToAcctMap.get(newAccountMapping) != null) {
910 newAccountMapping++;
911 }
912 userAcctIdToAcctMap.put(newAccountMapping, a);
913 return newAccountMapping;
914 }
915 }
916
917
918
919 /**
920 * Registers an application, represented by a UID, to support account types detailed in
921 * the applications manifest as well as allowing it to opt for notifications.
922 *
923 * @param uid UID of application
924 * @param ua UserAccount that currently hosts the account and application
925 */
926 private void registerAccountTypesSupported(int uid, UserAccounts ua) {
927 /* Account types supported are drawn from the Android Manifest of the Application */
928 String interestedPackages = null;
929 try {
930 String[] allPackages = mPackageManager.getPackagesForUid(uid);
931 for(String aPackage : allPackages) {
932 ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
933 PackageManager.GET_META_DATA);
934 Bundle b = ai.metaData;
935 if(b == null) {
936 return;
937 }
938 interestedPackages = b.getString("android.accounts.SupportedLoginTypes");
939 }
940 } catch (PackageManager.NameNotFoundException e) {
941 Log.d("NameNotFoundException", e.getMessage());
942 }
943 if(interestedPackages != null) {
944 /* request remote account types directly from here. Reads from Android Manifest */
945 requestAccountVisibility(interestedPackages.split(";"), uid, ua);
946 }
947 }
948
949 /**
950 * Allows AccountManager to register account types that an application has login
951 * support for. This method over-writes all of the application's previous settings
952 * for accounts it supported.
953 *
954 * @param accountTypes array of account types application wishes to support
955 * @param uid of application registering requested account types
956 * @param ua UserAccount that hosts the account and application
957 */
958 private void requestAccountVisibility(String[] accountTypes, int uid, UserAccounts ua) {
959 if(accountTypes.length > 0) {
960 clearRequestedAccountVisibility(uid, ua);
961 addRequestedAccountsVisibility(accountTypes, uid, ua);
962 }
963 }
964
965 /**
966 * Removes visibility of all Accounts to this particular UID. This is called when an
967 * application is uninstalled so another application that is installed with the same
968 * UID cannot access Accounts. This is called by AccountManager.
969 *
970 * @param uid of application to remove all Account visibility to
971 * @param ua UserAccount that hosts the current Account
972 */
973 private void removeAccountVisibilityAllAccounts(int uid, UserAccounts ua) {
974 synchronized(ua.cacheLock) {
975 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
976 ua.mVisibleListUidToMockAccountNumbers;
977 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
978 ArrayList<Integer> allAccountNumbersList = userWlUidToMockAccountNums.get(uid);
979 if(allAccountNumbersList != null) {
980 Integer[] allAccountNumbers = allAccountNumbersList.toArray(
981 new Integer[allAccountNumbersList.size()]);
982 for(int accountNum : allAccountNumbers) {
983 removeAccountVisibility(userAcctIdToAcctMap.get(accountNum), uid, ua);
984 }
985 }
986 }
987 }
988
989 /**
990 * Removes visible list functionality of a certain Account.
991 * This method is currently called by (1) addAccountExplicitly (as opposed to
992 * addAccountExplicitlyWithUid) and (2) removeAccountExplicitly.
993 *
994 * @param a the account to clear the visible list functionality for
995 * @param ua currently UserAccounts profile containing Account
996 *
997 * @return true if account previously had visible list functionality
998 */
999 private boolean removeVisibleListFunctionality(Account a, UserAccounts ua) {
1000 int mockAccountNum = getMockAccountNumber(a, ua);
1001 if(mockAccountNum < 0) {
1002 return false;
1003 }
1004 synchronized(ua.cacheLock) {
1005 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
1006 ua.mVisibleListUidToMockAccountNumbers;
1007 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
1008
1009 /* Removing mapping from account number to account removes visible list functionality*/
1010 userAcctIdToAcctMap.remove(mockAccountNum);
1011
1012 for(int i = userWlUidToMockAccountNums.size() - 1 ; i >= 0 ; i--) {
1013 int uidKey = userWlUidToMockAccountNums.keyAt(i);
1014 ArrayList<Integer> allAccountNumbers = userWlUidToMockAccountNums.get(uidKey);
1015 if(allAccountNumbers != null) {
1016 allAccountNumbers.remove(mockAccountNum);
1017 if(allAccountNumbers.isEmpty()) {
1018 userWlUidToMockAccountNums.remove(uidKey);
1019 }
1020 }
1021 }
1022 }
1023 return true;
1024 }
1025
1026 /**
1027 * Sends a direct intent to a package, notifying it of a visible account. This
1028 * method is a helper method of makeAccountVisible.
1029 *
1030 * @param desiredPackage to send Account to
1031 * @param visibleAccount to send to package
1032 */
1033 private void sendNotification(String desiredPackage, Account visibleAccount) {
1034 Intent intent = new Intent();
1035 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
1036 intent.setAction(NEW_ACCOUNT_VISIBLE);
1037 intent.setPackage(desiredPackage);
1038 intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
1039 mContext.sendBroadcast(intent);
1040 }
1041
1042 @Override
Dianne Hackborn164371f2013-10-01 19:10:13 -07001043 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
1044 throws RemoteException {
1045 try {
1046 return super.onTransact(code, data, reply, flags);
1047 } catch (RuntimeException e) {
1048 // The account manager only throws security exceptions, so let's
1049 // log all others.
1050 if (!(e instanceof SecurityException)) {
1051 Slog.wtf(TAG, "Account Manager Crash", e);
1052 }
1053 throw e;
1054 }
1055 }
1056
Kenny Root26ff6622012-07-30 12:58:03 -07001057 public void systemReady() {
Kenny Root26ff6622012-07-30 12:58:03 -07001058 }
1059
Amith Yamasani258848d2012-08-10 17:06:33 -07001060 private UserManager getUserManager() {
1061 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001062 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -07001063 }
1064 return mUserManager;
1065 }
1066
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001067 /**
1068 * Validate internal set of accounts against installed authenticators for
1069 * given user. Clears cached authenticators before validating.
1070 */
1071 public void validateAccounts(int userId) {
1072 final UserAccounts accounts = getUserAccounts(userId);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001073 // Invalidate user-specific cache to make sure we catch any
1074 // removed authenticators.
1075 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
1076 }
1077
1078 /**
1079 * Validate internal set of accounts against installed authenticators for
1080 * given user. Clear cached authenticators before validating when requested.
1081 */
1082 private void validateAccountsInternal(
1083 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001084 if (Log.isLoggable(TAG, Log.DEBUG)) {
1085 Log.d(TAG, "validateAccountsInternal " + accounts.userId
1086 + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001087 + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001088 }
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001089
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001090 if (invalidateAuthenticatorCache) {
1091 mAuthenticatorCache.invalidateCache(accounts.userId);
1092 }
1093
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001094 final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
1095 mAuthenticatorCache, accounts.userId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001096 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001097
Amith Yamasani04e0d262012-02-14 11:50:53 -08001098 synchronized (accounts.cacheLock) {
1099 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001100 boolean accountDeleted = false;
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001101
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001102 // Get a map of stored authenticator types to UID
1103 Map<String, Integer> metaAuthUid = AccountsDbUtils.findMetaAuthUid(db);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001104 // Create a list of authenticator type whose previous uid no longer exists
1105 HashSet<String> obsoleteAuthType = Sets.newHashSet();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001106 SparseBooleanArray knownUids = null;
1107 for (Entry<String, Integer> authToUidEntry : metaAuthUid.entrySet()) {
1108 String type = authToUidEntry.getKey();
1109 int uid = authToUidEntry.getValue();
1110 Integer knownUid = knownAuth.get(type);
1111 if (knownUid != null && uid == knownUid) {
1112 // Remove it from the knownAuth list if it's unchanged.
1113 knownAuth.remove(type);
1114 } else {
1115 /*
1116 * The authenticator is presently not cached and should only be triggered
1117 * when we think an authenticator has been removed (or is being updated).
1118 * But we still want to check if any data with the associated uid is
1119 * around. This is an (imperfect) signal that the package may be updating.
1120 *
1121 * A side effect of this is that an authenticator sharing a uid with
1122 * multiple apps won't get its credentials wiped as long as some app with
1123 * that uid is still on the device. But I suspect that this is a rare case.
1124 * And it isn't clear to me how an attacker could really exploit that
1125 * feature.
1126 *
1127 * The upshot is that we don't have to worry about accounts getting
1128 * uninstalled while the authenticator's package is being updated.
1129 *
1130 */
1131 if (knownUids == null) {
1132 knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001133 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001134 if (!knownUids.get(uid)) {
1135 // The authenticator is not presently available to the cache. And the
1136 // package no longer has a data directory (so we surmise it isn't updating).
1137 // So purge its data from the account databases.
1138 obsoleteAuthType.add(type);
1139 // And delete it from the TABLE_META
1140 AccountsDbUtils.deleteMetaByAuthTypeAndUid(db, type, uid);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001141 }
1142 }
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001143 }
1144
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001145 // Add the newly registered authenticator to TABLE_META. If old authenticators have
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001146 // been re-enabled (after being updated for example), then we just overwrite the old
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001147 // values.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001148 for (Entry<String, Integer> entry : knownAuth.entrySet()) {
1149 AccountsDbUtils.insertOrReplaceMetaAuthTypeAndUid(db, entry.getKey(),
1150 entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001151 }
1152
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001153 final Map<Long, Account> accountsMap = AccountsDbUtils.findAllAccounts(db);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001154 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001155 accounts.accountCache.clear();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001156 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001157 for (Entry<Long, Account> accountEntry : accountsMap.entrySet()) {
1158 final long accountId = accountEntry.getKey();
1159 final Account account = accountEntry.getValue();
1160 if (obsoleteAuthType.contains(account.type)) {
1161 Slog.w(TAG, "deleting account " + account.name + " because type "
1162 + account.type + "'s registered authenticator no longer exist.");
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001163 db.beginTransaction();
1164 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001165 AccountsDbUtils.deleteAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001166 // Also delete from CE table if user is unlocked; if user is currently
1167 // locked the account will be removed later by syncDeCeAccountsLocked
1168 if (userUnlocked) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001169 AccountsDbUtils.deleteCeAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001170 }
1171 db.setTransactionSuccessful();
1172 } finally {
1173 db.endTransaction();
1174 }
Fred Quintana56285a62010-12-02 14:20:51 -08001175 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001176
1177 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
1178 accountId, accounts);
1179
Amith Yamasani04e0d262012-02-14 11:50:53 -08001180 accounts.userDataCache.remove(account);
1181 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001182 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08001183 } else {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001184 ArrayList<String> accountNames = accountNamesByType.get(account.type);
Fred Quintana56285a62010-12-02 14:20:51 -08001185 if (accountNames == null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001186 accountNames = new ArrayList<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001187 accountNamesByType.put(account.type, accountNames);
Fred Quintana56285a62010-12-02 14:20:51 -08001188 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001189 accountNames.add(account.name);
Fred Quintana56285a62010-12-02 14:20:51 -08001190 }
1191 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001192 for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -08001193 final String accountType = cur.getKey();
1194 final ArrayList<String> accountNames = cur.getValue();
1195 final Account[] accountsForType = new Account[accountNames.size()];
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001196 for (int i = 0; i < accountsForType.length; i++) {
1197 accountsForType[i] = new Account(accountNames.get(i), accountType);
Fred Quintana56285a62010-12-02 14:20:51 -08001198 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001199 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -08001200 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001201 } finally {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001202 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001203 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001204 }
Fred Quintanaafa92b82009-12-01 16:27:03 -08001205 }
1206 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001207 }
1208
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001209 private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
1210 // Get the UIDs of all apps that might have data on the device. We want
1211 // to preserve user data if the app might otherwise be storing data.
1212 List<PackageInfo> pkgsWithData =
1213 mPackageManager.getInstalledPackagesAsUser(
1214 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
1215 SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
1216 for (PackageInfo pkgInfo : pkgsWithData) {
1217 if (pkgInfo.applicationInfo != null
1218 && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
1219 knownUids.put(pkgInfo.applicationInfo.uid, true);
1220 }
1221 }
1222 return knownUids;
1223 }
1224
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001225 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1226 Context context,
1227 int userId) {
1228 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001229 return getAuthenticatorTypeAndUIDForUser(authCache, userId);
1230 }
1231
1232 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1233 IAccountAuthenticatorCache authCache,
1234 int userId) {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001235 HashMap<String, Integer> knownAuth = new HashMap<>();
1236 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
1237 .getAllServices(userId)) {
1238 knownAuth.put(service.type.type, service.uid);
1239 }
1240 return knownAuth;
1241 }
1242
Amith Yamasani04e0d262012-02-14 11:50:53 -08001243 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001244 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001245 }
1246
1247 protected UserAccounts getUserAccounts(int userId) {
1248 synchronized (mUsers) {
1249 UserAccounts accounts = mUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001250 boolean validateAccounts = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001251 if (accounts == null) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001252 File preNDbFile = new File(getPreNDatabaseName(userId));
1253 File deDbFile = new File(getDeDatabaseName(userId));
1254 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001255 initializeDebugDbSizeAndCompileSqlStatementForLogging(
1256 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001257 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001258 purgeOldGrants(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001259 validateAccounts = true;
1260 }
1261 // open CE database if necessary
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001262 if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001263 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
1264 synchronized (accounts.cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001265 File preNDatabaseFile = new File(getPreNDatabaseName(userId));
1266 File ceDatabaseFile = new File(getCeDatabaseName(userId));
1267 CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
1268 accounts.openHelper.attachCeDatabase(ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001269 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001270 syncDeCeAccountsLocked(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001271 }
1272 if (validateAccounts) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001273 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001274 }
1275 return accounts;
1276 }
1277 }
1278
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001279 private void syncDeCeAccountsLocked(UserAccounts accounts) {
1280 Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
1281 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001282 List<Account> accountsToRemove = AccountsDbUtils.findCeAccountsNotInDe(db);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001283 if (!accountsToRemove.isEmpty()) {
1284 Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
1285 + accounts.userId + " was locked. Removing accounts from CE tables");
1286 logRecord(accounts, DebugDbHelper.ACTION_SYNC_DE_CE_ACCOUNTS, TABLE_ACCOUNTS);
1287
1288 for (Account account : accountsToRemove) {
1289 removeAccountInternal(accounts, account, Process.myUid());
1290 }
1291 }
1292 }
1293
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001294 private void purgeOldGrantsAll() {
1295 synchronized (mUsers) {
1296 for (int i = 0; i < mUsers.size(); i++) {
1297 purgeOldGrants(mUsers.valueAt(i));
1298 }
1299 }
1300 }
1301
1302 private void purgeOldGrants(UserAccounts accounts) {
1303 synchronized (accounts.cacheLock) {
1304 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001305 List<Integer> uids = AccountsDbUtils.findAllUidGrants(db);
1306 for (int uid : uids) {
1307 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
1308 if (packageExists) {
1309 continue;
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001310 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001311 Log.d(TAG, "deleting grants for UID " + uid
1312 + " because its package is no longer installed");
1313 AccountsDbUtils.deleteGrantsByUid(db, uid);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001314 }
1315 }
1316 }
1317
Amith Yamasani13593602012-03-22 16:16:17 -07001318 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -07001319 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -07001320 if (userId < 1) return;
1321
1322 UserAccounts accounts;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001323 boolean userUnlocked;
Amith Yamasani13593602012-03-22 16:16:17 -07001324 synchronized (mUsers) {
1325 accounts = mUsers.get(userId);
1326 mUsers.remove(userId);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001327 userUnlocked = mLocalUnlockedUsers.get(userId);
1328 mLocalUnlockedUsers.delete(userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001329 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001330 if (accounts != null) {
1331 synchronized (accounts.cacheLock) {
1332 accounts.openHelper.close();
1333 }
Amith Yamasani13593602012-03-22 16:16:17 -07001334 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001335 Log.i(TAG, "Removing database files for user " + userId);
1336 File dbFile = new File(getDeDatabaseName(userId));
Amith Yamasani13593602012-03-22 16:16:17 -07001337
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001338 deleteDbFileWarnIfFailed(dbFile);
1339 // Remove CE file if user is unlocked, or FBE is not enabled
1340 boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
1341 if (!fbeEnabled || userUnlocked) {
1342 File ceDb = new File(getCeDatabaseName(userId));
1343 if (ceDb.exists()) {
1344 deleteDbFileWarnIfFailed(ceDb);
1345 }
1346 }
1347 }
1348
1349 private static void deleteDbFileWarnIfFailed(File dbFile) {
1350 if (!SQLiteDatabase.deleteDatabase(dbFile)) {
1351 Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
Amith Yamasani13593602012-03-22 16:16:17 -07001352 }
1353 }
1354
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001355 @VisibleForTesting
1356 void onUserUnlocked(Intent intent) {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -06001357 onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
1358 }
1359
1360 void onUnlockUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001361 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1362 Log.v(TAG, "onUserUnlocked " + userId);
1363 }
1364 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001365 mLocalUnlockedUsers.put(userId, true);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001366 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001367 if (userId < 1) return;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001368 syncSharedAccounts(userId);
1369 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001370
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001371 private void syncSharedAccounts(int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001372 // Check if there's a shared account that needs to be created as an account
1373 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
1374 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -07001375 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001376 int parentUserId = UserManager.isSplitSystemUser()
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001377 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001378 : UserHandle.USER_SYSTEM;
1379 if (parentUserId < 0) {
1380 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
1381 return;
1382 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001383 for (Account sa : sharedAccounts) {
1384 if (ArrayUtils.contains(accounts, sa)) continue;
1385 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001386 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001387 }
1388 }
1389
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001390 @Override
1391 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001392 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -07001393 }
1394
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001395 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001396 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001397 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001398 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1399 Log.v(TAG, "getPassword: " + account
1400 + ", caller's uid " + Binder.getCallingUid()
1401 + ", pid " + Binder.getCallingPid());
1402 }
Fred Quintana382601f2010-03-25 12:25:10 -07001403 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001404 int userId = UserHandle.getCallingUserId();
1405 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001406 String msg = String.format(
1407 "uid %s cannot get secrets for accounts of type: %s",
1408 callingUid,
1409 account.type);
1410 throw new SecurityException(msg);
1411 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001412 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001413 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001414 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001415 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001416 } finally {
1417 restoreCallingIdentity(identityToken);
1418 }
1419 }
1420
Amith Yamasani04e0d262012-02-14 11:50:53 -08001421 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -07001422 if (account == null) {
1423 return null;
1424 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001425 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001426 Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
1427 return null;
1428 }
Fred Quintana31957f12009-10-21 13:43:10 -07001429
Amith Yamasani04e0d262012-02-14 11:50:53 -08001430 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001431 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001432 return AccountsDbUtils.findAccountPasswordByNameAndType(db, account.name,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001433 account.type);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001434 }
1435 }
1436
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001437 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001438 public String getPreviousName(Account account) {
1439 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1440 Log.v(TAG, "getPreviousName: " + account
1441 + ", caller's uid " + Binder.getCallingUid()
1442 + ", pid " + Binder.getCallingPid());
1443 }
1444 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001445 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001446 long identityToken = clearCallingIdentity();
1447 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001448 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001449 return readPreviousNameInternal(accounts, account);
1450 } finally {
1451 restoreCallingIdentity(identityToken);
1452 }
1453 }
1454
1455 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
1456 if (account == null) {
1457 return null;
1458 }
1459 synchronized (accounts.cacheLock) {
1460 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
1461 if (previousNameRef == null) {
1462 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001463 String previousName = AccountsDbUtils.findAccountPreviousName(db, account);
1464 previousNameRef = new AtomicReference<>(previousName);
1465 accounts.previousNameCache.put(account, previousNameRef);
1466 return previousName;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001467 } else {
1468 return previousNameRef.get();
1469 }
1470 }
1471 }
1472
1473 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001474 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001475 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001476 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001477 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
1478 account, key, callingUid, Binder.getCallingPid());
1479 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -08001480 }
Fred Quintana382601f2010-03-25 12:25:10 -07001481 if (account == null) throw new IllegalArgumentException("account is null");
1482 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001483 int userId = UserHandle.getCallingUserId();
1484 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001485 String msg = String.format(
1486 "uid %s cannot get user data for accounts of type: %s",
1487 callingUid,
1488 account.type);
1489 throw new SecurityException(msg);
1490 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001491 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07001492 Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
1493 return null;
1494 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001495 long identityToken = clearCallingIdentity();
1496 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001497 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00001498 synchronized (accounts.cacheLock) {
1499 if (!accountExistsCacheLocked(accounts, account)) {
1500 return null;
1501 }
1502 return readUserDataInternalLocked(accounts, account, key);
1503 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001504 } finally {
1505 restoreCallingIdentity(identityToken);
1506 }
1507 }
1508
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001509 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001510 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001511 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001512 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1513 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001514 + "for user id " + userId
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001515 + " caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001516 + ", pid " + Binder.getCallingPid());
1517 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001518 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001519 if (isCrossUser(callingUid, userId)) {
1520 throw new SecurityException(
1521 String.format(
1522 "User %s tying to get authenticator types for %s" ,
1523 UserHandle.getCallingUserId(),
1524 userId));
1525 }
1526
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001527 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001528 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001529 return getAuthenticatorTypesInternal(userId);
1530
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001531 } finally {
1532 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001533 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001534 }
1535
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001536 /**
1537 * Should only be called inside of a clearCallingIdentity block.
1538 */
1539 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
1540 Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
1541 authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
1542 AuthenticatorDescription[] types =
1543 new AuthenticatorDescription[authenticatorCollection.size()];
1544 int i = 0;
1545 for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
1546 : authenticatorCollection) {
1547 types[i] = authenticator.type;
1548 i++;
1549 }
1550 return types;
1551 }
1552
1553
1554
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001555 private boolean isCrossUser(int callingUid, int userId) {
1556 return (userId != UserHandle.getCallingUserId()
1557 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001558 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001559 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1560 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001561 }
1562
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001563 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07001564 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001565 Bundle.setDefusable(extras, true);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001566 // clears the visible list functionality for this account because this method allows
1567 // default account access to all applications for account.
1568
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001569 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001570 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001571 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001572 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001573 + ", pid " + Binder.getCallingPid());
1574 }
Fred Quintana382601f2010-03-25 12:25:10 -07001575 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001576 int userId = UserHandle.getCallingUserId();
1577 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001578 String msg = String.format(
1579 "uid %s cannot explicitly add accounts of type: %s",
1580 callingUid,
1581 account.type);
1582 throw new SecurityException(msg);
1583 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001584 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001585 /*
1586 * Child users are not allowed to add accounts. Only the accounts that are
1587 * shared by the parent profile can be added to child profile.
1588 *
1589 * TODO: Only allow accounts that were shared to be added by
1590 * a limited user.
1591 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001592
Fred Quintana60307342009-03-24 22:48:12 -07001593 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001594 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001595 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001596 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001597 return addAccountInternal(accounts, account, password, extras, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -07001598 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001599 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001600 }
1601 }
1602
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001603 @Override
1604 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001605 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001606 int callingUid = Binder.getCallingUid();
1607 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1608 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001609 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001610 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001611 final UserAccounts fromAccounts = getUserAccounts(userFrom);
1612 final UserAccounts toAccounts = getUserAccounts(userTo);
1613 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001614 if (response != null) {
1615 Bundle result = new Bundle();
1616 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1617 try {
1618 response.onResult(result);
1619 } catch (RemoteException e) {
1620 Slog.w(TAG, "Failed to report error back to the client." + e);
1621 }
1622 }
1623 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -08001624 }
1625
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001626 Slog.d(TAG, "Copying account " + account.name
1627 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001628 long identityToken = clearCallingIdentity();
1629 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001630 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001631 false /* stripAuthTokenFromResult */, account.name,
1632 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001633 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001634 protected String toDebugString(long now) {
1635 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1636 + ", " + account.type;
1637 }
1638
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001639 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001640 public void run() throws RemoteException {
1641 mAuthenticator.getAccountCredentialsForCloning(this, account);
1642 }
1643
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001644 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001645 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001646 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001647 if (result != null
1648 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1649 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001650 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001651 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001652 super.onResult(result);
1653 }
1654 }
1655 }.bind();
1656 } finally {
1657 restoreCallingIdentity(identityToken);
1658 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001659 }
1660
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001661 @Override
1662 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001663 final int callingUid = Binder.getCallingUid();
1664 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1665 String msg = String.format(
1666 "accountAuthenticated( account: %s, callerUid: %s)",
1667 account,
1668 callingUid);
1669 Log.v(TAG, msg);
1670 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001671 if (account == null) {
1672 throw new IllegalArgumentException("account is null");
1673 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001674 int userId = UserHandle.getCallingUserId();
1675 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001676 String msg = String.format(
1677 "uid %s cannot notify authentication for accounts of type: %s",
1678 callingUid,
1679 account.type);
1680 throw new SecurityException(msg);
1681 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001682
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001683 if (!canUserModifyAccounts(userId, callingUid) ||
1684 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001685 return false;
1686 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001687
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001688 long identityToken = clearCallingIdentity();
1689 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001690 UserAccounts accounts = getUserAccounts(userId);
1691 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001692 } finally {
1693 restoreCallingIdentity(identityToken);
1694 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07001695 }
1696
1697 private boolean updateLastAuthenticatedTime(Account account) {
1698 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001699 synchronized (accounts.cacheLock) {
1700 final ContentValues values = new ContentValues();
1701 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
1702 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1703 int i = db.update(
1704 TABLE_ACCOUNTS,
1705 values,
1706 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1707 new String[] {
1708 account.name, account.type
1709 });
1710 if (i > 0) {
1711 return true;
1712 }
1713 }
1714 return false;
1715 }
1716
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001717 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001718 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1719 final int parentUserId){
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001720 Bundle.setDefusable(accountCredentials, true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001721 long id = clearCallingIdentity();
1722 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001723 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001724 false /* stripAuthTokenFromResult */, account.name,
1725 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001726 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001727 protected String toDebugString(long now) {
1728 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1729 + ", " + account.type;
1730 }
1731
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001732 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001733 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001734 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001735 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001736 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -07001737 for (Account acc : getAccounts(parentUserId,
1738 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001739 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001740 mAuthenticator.addAccountFromCredentials(
1741 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001742 break;
1743 }
1744 }
1745 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001746 }
1747
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001748 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001749 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001750 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001751 // TODO: Anything to do if if succedded?
1752 // TODO: If it failed: Show error notification? Should we remove the shadow
1753 // account to avoid retries?
1754 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001755 }
1756
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001757 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001758 public void onError(int errorCode, String errorMessage) {
1759 super.onError(errorCode, errorMessage);
1760 // TODO: Show error notification to user
1761 // TODO: Should we remove the shadow account so that it doesn't keep trying?
1762 }
1763
1764 }.bind();
1765 } finally {
1766 restoreCallingIdentity(id);
1767 }
1768 }
1769
Amith Yamasani04e0d262012-02-14 11:50:53 -08001770 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001771 Bundle extras, int callingUid) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001772 Bundle.setDefusable(extras, true);
Fred Quintana743dfad2010-07-15 10:59:25 -07001773 if (account == null) {
1774 return false;
1775 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001776 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001777 Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
1778 + " is locked. callingUid=" + callingUid);
1779 return false;
1780 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001781 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001782 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001783 db.beginTransaction();
1784 try {
1785 long numMatches = DatabaseUtils.longForQuery(db,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001786 "select count(*) from " + CE_TABLE_ACCOUNTS
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001787 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1788 new String[]{account.name, account.type});
1789 if (numMatches > 0) {
1790 Log.w(TAG, "insertAccountIntoDatabase: " + account
1791 + ", skipping since the account already exists");
1792 return false;
1793 }
1794 ContentValues values = new ContentValues();
1795 values.put(ACCOUNTS_NAME, account.name);
1796 values.put(ACCOUNTS_TYPE, account.type);
1797 values.put(ACCOUNTS_PASSWORD, password);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001798 long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001799 if (accountId < 0) {
1800 Log.w(TAG, "insertAccountIntoDatabase: " + account
1801 + ", skipping the DB insert failed");
1802 return false;
1803 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001804 // Insert into DE table
1805 values = new ContentValues();
1806 values.put(ACCOUNTS_ID, accountId);
1807 values.put(ACCOUNTS_NAME, account.name);
1808 values.put(ACCOUNTS_TYPE, account.type);
1809 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS,
1810 System.currentTimeMillis());
1811 if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) {
1812 Log.w(TAG, "insertAccountIntoDatabase: " + account
1813 + ", skipping the DB insert failed");
1814 return false;
1815 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001816 if (extras != null) {
1817 for (String key : extras.keySet()) {
1818 final String value = extras.getString(key);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001819 if (AccountsDbUtils.insertExtra(db, accountId, key, value) < 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001820 Log.w(TAG, "insertAccountIntoDatabase: " + account
1821 + ", skipping since insertExtra failed for key " + key);
1822 return false;
1823 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001824 }
1825 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001826 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001827
1828 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
1829 accounts, callingUid);
1830
Amith Yamasani04e0d262012-02-14 11:50:53 -08001831 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001832 } finally {
1833 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001834 }
Amith Yamasani5be347b2013-03-31 17:44:31 -07001835 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001836 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1837 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001838 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001839
1840 // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1841 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001842 return true;
1843 }
1844
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001845 private boolean isLocalUnlockedUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001846 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001847 return mLocalUnlockedUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001848 }
1849 }
1850
Amith Yamasani5be347b2013-03-31 17:44:31 -07001851 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001852 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -07001853 * running, then clone the account too.
1854 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001855 *
Amith Yamasani5be347b2013-03-31 17:44:31 -07001856 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001857 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -07001858 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -07001859 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001860 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001861 addSharedAccountAsUser(account, user.id);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001862 if (isLocalUnlockedUser(user.id)) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07001863 mHandler.sendMessage(mHandler.obtainMessage(
Fyodor Kupolov041232a2016-02-22 15:01:45 -08001864 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -07001865 }
1866 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001867 }
1868 }
1869
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001870 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001871 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001872 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001873 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001874 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1875 Log.v(TAG, "hasFeatures: " + account
1876 + ", response " + response
1877 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001878 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001879 + ", pid " + Binder.getCallingPid());
1880 }
Fred Quintana382601f2010-03-25 12:25:10 -07001881 if (response == null) throw new IllegalArgumentException("response is null");
1882 if (account == null) throw new IllegalArgumentException("account is null");
1883 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001884 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001885 checkReadAccountsPermitted(callingUid, account.type, userId,
1886 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001887
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001888 long identityToken = clearCallingIdentity();
1889 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001890 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001891 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001892 } finally {
1893 restoreCallingIdentity(identityToken);
1894 }
1895 }
1896
1897 private class TestFeaturesSession extends Session {
1898 private final String[] mFeatures;
1899 private final Account mAccount;
1900
Amith Yamasani04e0d262012-02-14 11:50:53 -08001901 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001902 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001903 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001904 true /* stripAuthTokenFromResult */, account.name,
1905 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001906 mFeatures = features;
1907 mAccount = account;
1908 }
1909
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001910 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001911 public void run() throws RemoteException {
1912 try {
1913 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1914 } catch (RemoteException e) {
1915 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1916 }
1917 }
1918
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001919 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001920 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001921 Bundle.setDefusable(result, true);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001922 IAccountManagerResponse response = getResponseAndClose();
1923 if (response != null) {
1924 try {
1925 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001926 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001927 return;
1928 }
Fred Quintana56285a62010-12-02 14:20:51 -08001929 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1930 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1931 + response);
1932 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001933 final Bundle newResult = new Bundle();
1934 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1935 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1936 response.onResult(newResult);
1937 } catch (RemoteException e) {
1938 // if the caller is dead then there is no one to care about remote exceptions
1939 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1940 Log.v(TAG, "failure while notifying response", e);
1941 }
1942 }
1943 }
1944 }
1945
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001946 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001947 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001948 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001949 + ", " + mAccount
1950 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1951 }
1952 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001953
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001954 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001955 public void renameAccount(
1956 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001957 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001958 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1959 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001960 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001961 + ", pid " + Binder.getCallingPid());
1962 }
1963 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001964 int userId = UserHandle.getCallingUserId();
1965 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001966 String msg = String.format(
1967 "uid %s cannot rename accounts of type: %s",
1968 callingUid,
1969 accountToRename.type);
1970 throw new SecurityException(msg);
1971 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001972 long identityToken = clearCallingIdentity();
1973 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001974 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001975 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001976 Bundle result = new Bundle();
1977 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1978 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
1979 try {
1980 response.onResult(result);
1981 } catch (RemoteException e) {
1982 Log.w(TAG, e.getMessage());
1983 }
1984 } finally {
1985 restoreCallingIdentity(identityToken);
1986 }
1987 }
1988
1989 private Account renameAccountInternal(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001990 UserAccounts accounts, Account accountToRename, String newName) {
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001991 Account resultAccount = null;
1992 /*
1993 * Cancel existing notifications. Let authenticators
1994 * re-post notifications as required. But we don't know if
1995 * the authenticators have bound their notifications to
1996 * now stale account name data.
1997 *
1998 * With a rename api, we might not need to do this anymore but it
1999 * shouldn't hurt.
2000 */
2001 cancelNotification(
2002 getSigninRequiredNotificationId(accounts, accountToRename),
2003 new UserHandle(accounts.userId));
2004 synchronized(accounts.credentialsPermissionNotificationIds) {
2005 for (Pair<Pair<Account, String>, Integer> pair:
2006 accounts.credentialsPermissionNotificationIds.keySet()) {
2007 if (accountToRename.equals(pair.first.first)) {
2008 int id = accounts.credentialsPermissionNotificationIds.get(pair);
2009 cancelNotification(id, new UserHandle(accounts.userId));
2010 }
2011 }
2012 }
2013 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002014 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002015 db.beginTransaction();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002016 Account renamedAccount = new Account(newName, accountToRename.type);
2017 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002018 final long accountId = AccountsDbUtils.findAccountId(db, accountToRename);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002019 if (accountId >= 0) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002020 final ContentValues values = new ContentValues();
2021 values.put(ACCOUNTS_NAME, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002022 final String[] argsAccountId = { String.valueOf(accountId) };
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002023 db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
2024 // Update NAME/PREVIOUS_NAME in DE accounts table
2025 values.put(ACCOUNTS_PREVIOUS_NAME, accountToRename.name);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002026 db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
2027 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002028 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_ACCOUNTS, accountId,
2029 accounts);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002030 }
2031 } finally {
2032 db.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002033 }
2034 /*
2035 * Database transaction was successful. Clean up cached
2036 * data associated with the account in the user profile.
2037 */
2038 insertAccountIntoCacheLocked(accounts, renamedAccount);
2039 /*
2040 * Extract the data and token caches before removing the
2041 * old account to preserve the user data associated with
2042 * the account.
2043 */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002044 Map<String, String> tmpData = accounts.userDataCache.get(accountToRename);
2045 Map<String, String> tmpTokens = accounts.authTokenCache.get(accountToRename);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002046 removeAccountFromCacheLocked(accounts, accountToRename);
2047 /*
2048 * Update the cached data associated with the renamed
2049 * account.
2050 */
2051 accounts.userDataCache.put(renamedAccount, tmpData);
2052 accounts.authTokenCache.put(renamedAccount, tmpTokens);
2053 accounts.previousNameCache.put(
2054 renamedAccount,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002055 new AtomicReference<>(accountToRename.name));
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002056 resultAccount = renamedAccount;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002057
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002058 int parentUserId = accounts.userId;
2059 if (canHaveProfile(parentUserId)) {
2060 /*
2061 * Owner or system user account was renamed, rename the account for
2062 * those users with which the account was shared.
2063 */
2064 List<UserInfo> users = getUserManager().getUsers(true);
2065 for (UserInfo user : users) {
2066 if (user.isRestricted()
2067 && (user.restrictedProfileParentId == parentUserId)) {
2068 renameSharedAccountAsUser(accountToRename, newName, user.id);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002069 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002070 }
2071 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002072 sendAccountsChangedBroadcast(accounts.userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002073 }
2074 return resultAccount;
2075 }
2076
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002077 private boolean canHaveProfile(final int parentUserId) {
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002078 final UserInfo userInfo = getUserManager().getUserInfo(parentUserId);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002079 return userInfo != null && userInfo.canHaveProfile();
2080 }
2081
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07002082 @Override
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002083 public void removeAccount(IAccountManagerResponse response, Account account,
2084 boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002085 removeAccountAsUser(
2086 response,
2087 account,
2088 expectActivityLaunch,
2089 UserHandle.getCallingUserId());
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002090 }
2091
2092 @Override
2093 public void removeAccountAsUser(IAccountManagerResponse response, Account account,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002094 boolean expectActivityLaunch, int userId) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002095 final int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002096 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2097 Log.v(TAG, "removeAccount: " + account
2098 + ", response " + response
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002099 + ", caller's uid " + callingUid
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002100 + ", pid " + Binder.getCallingPid()
2101 + ", for user id " + userId);
2102 }
2103 if (response == null) throw new IllegalArgumentException("response is null");
2104 if (account == null) throw new IllegalArgumentException("account is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002105 // Only allow the system process to modify accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002106 if (isCrossUser(callingUid, userId)) {
2107 throw new SecurityException(
2108 String.format(
2109 "User %s tying remove account for %s" ,
2110 UserHandle.getCallingUserId(),
2111 userId));
2112 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002113 /*
2114 * Only the system or authenticator should be allowed to remove accounts for that
2115 * authenticator. This will let users remove accounts (via Settings in the system) but not
2116 * arbitrary applications (like competing authenticators).
2117 */
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002118 UserHandle user = UserHandle.of(userId);
Ian Pedowitz358e51f2016-03-15 17:08:27 +00002119 if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier())
2120 && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002121 String msg = String.format(
2122 "uid %s cannot remove accounts of type: %s",
2123 callingUid,
2124 account.type);
2125 throw new SecurityException(msg);
2126 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002127 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002128 try {
2129 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
2130 "User cannot modify accounts");
2131 } catch (RemoteException re) {
2132 }
2133 return;
2134 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00002135 if (!canUserModifyAccountsForType(userId, account.type, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002136 try {
2137 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
2138 "User cannot modify accounts of this type (policy).");
2139 } catch (RemoteException re) {
2140 }
2141 return;
2142 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002143 long identityToken = clearCallingIdentity();
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002144 UserAccounts accounts = getUserAccounts(userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01002145 cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002146 synchronized(accounts.credentialsPermissionNotificationIds) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002147 for (Pair<Pair<Account, String>, Integer> pair:
Amith Yamasani04e0d262012-02-14 11:50:53 -08002148 accounts.credentialsPermissionNotificationIds.keySet()) {
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002149 if (account.equals(pair.first.first)) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002150 int id = accounts.credentialsPermissionNotificationIds.get(pair);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002151 cancelNotification(id, user);
Costin Manolacheec0c4f42010-11-16 09:57:28 -08002152 }
2153 }
2154 }
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002155 SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002156 final long accountId = AccountsDbUtils.findAccountId(db, account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002157 logRecord(
2158 db,
2159 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
2160 TABLE_ACCOUNTS,
2161 accountId,
2162 accounts,
2163 callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002164 try {
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002165 new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
2166 } finally {
2167 restoreCallingIdentity(identityToken);
2168 }
2169 }
2170
2171 @Override
2172 public boolean removeAccountExplicitly(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002173 final int callingUid = Binder.getCallingUid();
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002174 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2175 Log.v(TAG, "removeAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002176 + ", caller's uid " + callingUid
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002177 + ", pid " + Binder.getCallingPid());
2178 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002179 int userId = Binder.getCallingUserHandle().getIdentifier();
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002180 if (account == null) {
2181 /*
2182 * Null accounts should result in returning false, as per
2183 * AccountManage.addAccountExplicitly(...) java doc.
2184 */
2185 Log.e(TAG, "account is null");
2186 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002187 } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002188 String msg = String.format(
2189 "uid %s cannot explicitly add accounts of type: %s",
2190 callingUid,
2191 account.type);
2192 throw new SecurityException(msg);
2193 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07002194 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002195 UserAccounts accounts = getUserAccountsForCaller();
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002196 SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002197 final long accountId = AccountsDbUtils.findAccountId(db, account);
Dmitry Dementyeve59fc5f2016-07-08 10:46:22 -07002198 logRecord(
2199 db,
2200 DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE,
2201 TABLE_ACCOUNTS,
2202 accountId,
2203 accounts,
2204 callingUid);
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002205 long identityToken = clearCallingIdentity();
2206 try {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002207 return removeAccountInternal(accounts, account, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002208 } finally {
2209 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07002210 }
Fred Quintana60307342009-03-24 22:48:12 -07002211 }
2212
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002213 private class RemoveAccountSession extends Session {
2214 final Account mAccount;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002215 public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
Simranjit Singh Kohli8778f992014-11-05 21:33:55 -08002216 Account account, boolean expectActivityLaunch) {
2217 super(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002218 true /* stripAuthTokenFromResult */, account.name,
2219 false /* authDetailsRequired */);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002220 mAccount = account;
2221 }
2222
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002223 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002224 protected String toDebugString(long now) {
2225 return super.toDebugString(now) + ", removeAccount"
2226 + ", account " + mAccount;
2227 }
2228
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002229 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002230 public void run() throws RemoteException {
2231 mAuthenticator.getAccountRemovalAllowed(this, mAccount);
2232 }
2233
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002234 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002235 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002236 Bundle.setDefusable(result, true);
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002237 if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
2238 && !result.containsKey(AccountManager.KEY_INTENT)) {
2239 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002240 if (removalAllowed) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002241 removeAccountInternal(mAccounts, mAccount, getCallingUid());
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002242 }
2243 IAccountManagerResponse response = getResponseAndClose();
2244 if (response != null) {
Fred Quintana56285a62010-12-02 14:20:51 -08002245 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2246 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2247 + response);
2248 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002249 Bundle result2 = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002250 result2.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002251 try {
2252 response.onResult(result2);
2253 } catch (RemoteException e) {
2254 // ignore
2255 }
2256 }
2257 }
2258 super.onResult(result);
2259 }
2260 }
2261
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07002262 @VisibleForTesting
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002263 protected void removeAccountInternal(Account account) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002264 removeAccountInternal(getUserAccountsForCaller(), account, getCallingUid());
Amith Yamasani04e0d262012-02-14 11:50:53 -08002265 }
2266
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002267 private boolean removeAccountInternal(UserAccounts accounts, Account account, int callingUid) {
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002268 boolean isChanged = false;
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002269 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002270 if (!userUnlocked) {
2271 Slog.i(TAG, "Removing account " + account + " while user "+ accounts.userId
2272 + " is still locked. CE data will be removed later");
2273 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08002274 synchronized (accounts.cacheLock) {
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002275 final SQLiteDatabase db = userUnlocked
2276 ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
2277 : accounts.openHelper.getWritableDatabase();
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002278 db.beginTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002279 // Set to a dummy value, this will only be used if the database
2280 // transaction succeeds.
2281 long accountId = -1;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002282 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002283 accountId = AccountsDbUtils.findAccountId(db, account);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002284 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002285 AccountsDbUtils.deleteAccount(db, accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002286 if (userUnlocked) {
2287 // Delete from CE table
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002288 AccountsDbUtils.deleteCeAccount(db, accountId);
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002289 }
2290 db.setTransactionSuccessful();
2291 isChanged = true;
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002292 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07002293 } finally {
2294 db.endTransaction();
2295 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002296 if (isChanged) {
2297 removeAccountFromCacheLocked(accounts, account);
2298 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
2299 sendAccountsChangedBroadcast(accounts.userId);
2300 String action = userUnlocked ? DebugDbHelper.ACTION_ACCOUNT_REMOVE
2301 : DebugDbHelper.ACTION_ACCOUNT_REMOVE_DE;
2302 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts);
2303 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002304 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002305 long id = Binder.clearCallingIdentity();
2306 try {
2307 int parentUserId = accounts.userId;
2308 if (canHaveProfile(parentUserId)) {
2309 // Remove from any restricted profiles that are sharing this account.
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07002310 List<UserInfo> users = getUserManager().getUsers(true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002311 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002312 if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002313 removeSharedAccountAsUser(account, user.id, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002314 }
2315 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08002316 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07002317 } finally {
2318 Binder.restoreCallingIdentity(id);
Amith Yamasani67df64b2012-12-14 12:09:36 -08002319 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002320
2321 if (isChanged) {
2322 synchronized (accounts.credentialsPermissionNotificationIds) {
2323 for (Pair<Pair<Account, String>, Integer> key
2324 : accounts.credentialsPermissionNotificationIds.keySet()) {
2325 if (account.equals(key.first.first)
2326 && AccountManager.ACCOUNT_ACCESS_TOKEN.equals(key.first.second)) {
2327 final int uid = (Integer) key.second;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07002328 mHandler.post(() -> cancelAccountAccessRequestNotificationIfNeeded(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002329 account, uid, false));
2330 }
2331 }
2332 }
2333 }
2334
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002335 return isChanged;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002336 }
2337
Maggie Benthalla12fccf2013-03-14 18:02:12 -04002338 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002339 public void invalidateAuthToken(String accountType, String authToken) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002340 int callerUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002341 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2342 Log.v(TAG, "invalidateAuthToken: accountType " + accountType
Carlos Valdivia91979be2015-05-22 14:11:35 -07002343 + ", caller's uid " + callerUid
Fred Quintana56285a62010-12-02 14:20:51 -08002344 + ", pid " + Binder.getCallingPid());
2345 }
Fred Quintana382601f2010-03-25 12:25:10 -07002346 if (accountType == null) throw new IllegalArgumentException("accountType is null");
2347 if (authToken == null) throw new IllegalArgumentException("authToken is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002348 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002349 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002350 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002351 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002352 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002353 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002354 db.beginTransaction();
2355 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002356 invalidateAuthTokenLocked(accounts, db, accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002357 invalidateCustomTokenLocked(accounts, accountType, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002358 db.setTransactionSuccessful();
2359 } finally {
2360 db.endTransaction();
2361 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002362 }
Fred Quintana60307342009-03-24 22:48:12 -07002363 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002364 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002365 }
2366 }
2367
Carlos Valdivia91979be2015-05-22 14:11:35 -07002368 private void invalidateCustomTokenLocked(
2369 UserAccounts accounts,
2370 String accountType,
2371 String authToken) {
2372 if (authToken == null || accountType == null) {
2373 return;
2374 }
2375 // Also wipe out cached token in memory.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002376 accounts.accountTokenCaches.remove(accountType, authToken);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002377 }
2378
Amith Yamasani04e0d262012-02-14 11:50:53 -08002379 private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
2380 String accountType, String authToken) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002381 if (authToken == null || accountType == null) {
2382 return;
2383 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002384 Cursor cursor = AccountsDbUtils.findAuthtokenForAllAccounts(db, accountType, authToken);
Fred Quintana33269202009-04-20 16:05:10 -07002385 try {
2386 while (cursor.moveToNext()) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002387 String authTokenId = cursor.getString(0);
Fred Quintana33269202009-04-20 16:05:10 -07002388 String accountName = cursor.getString(1);
2389 String authTokenType = cursor.getString(2);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002390 AccountsDbUtils.deleteAuthToken(db, authTokenId);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002391 writeAuthTokenIntoCacheLocked(
2392 accounts,
2393 db,
2394 new Account(accountName, accountType),
2395 authTokenType,
2396 null);
Fred Quintana60307342009-03-24 22:48:12 -07002397 }
Fred Quintana33269202009-04-20 16:05:10 -07002398 } finally {
2399 cursor.close();
Fred Quintana60307342009-03-24 22:48:12 -07002400 }
2401 }
2402
Carlos Valdivia91979be2015-05-22 14:11:35 -07002403 private void saveCachedToken(
2404 UserAccounts accounts,
2405 Account account,
2406 String callerPkg,
2407 byte[] callerSigDigest,
2408 String tokenType,
2409 String token,
2410 long expiryMillis) {
2411
2412 if (account == null || tokenType == null || callerPkg == null || callerSigDigest == null) {
2413 return;
2414 }
2415 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002416 UserHandle.of(accounts.userId));
Carlos Valdivia91979be2015-05-22 14:11:35 -07002417 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002418 accounts.accountTokenCaches.put(
2419 account, token, tokenType, callerPkg, callerSigDigest, expiryMillis);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002420 }
2421 }
2422
Amith Yamasani04e0d262012-02-14 11:50:53 -08002423 private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
2424 String authToken) {
Fred Quintana31957f12009-10-21 13:43:10 -07002425 if (account == null || type == null) {
2426 return false;
2427 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07002428 cancelNotification(getSigninRequiredNotificationId(accounts, account),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002429 UserHandle.of(accounts.userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08002430 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002431 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002432 db.beginTransaction();
2433 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002434 long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002435 if (accountId < 0) {
2436 return false;
2437 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002438 AccountsDbUtils.deleteAuthtokensByAccountIdAndType(db, accountId, type);
2439 if (AccountsDbUtils.insertAuthToken(db, accountId, type, authToken) >= 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002440 db.setTransactionSuccessful();
Amith Yamasani04e0d262012-02-14 11:50:53 -08002441 writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002442 return true;
2443 }
Fred Quintana33269202009-04-20 16:05:10 -07002444 return false;
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002445 } finally {
2446 db.endTransaction();
Fred Quintana33269202009-04-20 16:05:10 -07002447 }
Fred Quintana60307342009-03-24 22:48:12 -07002448 }
2449 }
2450
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002451 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002452 public String peekAuthToken(Account account, String authTokenType) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002453 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002454 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2455 Log.v(TAG, "peekAuthToken: " + account
2456 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002457 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002458 + ", pid " + Binder.getCallingPid());
2459 }
Fred Quintana382601f2010-03-25 12:25:10 -07002460 if (account == null) throw new IllegalArgumentException("account is null");
2461 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002462 int userId = UserHandle.getCallingUserId();
2463 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002464 String msg = String.format(
2465 "uid %s cannot peek the authtokens associated with accounts of type: %s",
2466 callingUid,
2467 account.type);
2468 throw new SecurityException(msg);
2469 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06002470 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07002471 Log.w(TAG, "Authtoken not available - user " + userId + " data is locked. callingUid "
2472 + callingUid);
2473 return null;
2474 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002475 long identityToken = clearCallingIdentity();
2476 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002477 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002478 return readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002479 } finally {
2480 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002481 }
Fred Quintana60307342009-03-24 22:48:12 -07002482 }
2483
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002484 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002485 public void setAuthToken(Account account, String authTokenType, String authToken) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002486 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002487 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2488 Log.v(TAG, "setAuthToken: " + account
2489 + ", authTokenType " + authTokenType
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002490 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002491 + ", pid " + Binder.getCallingPid());
2492 }
Fred Quintana382601f2010-03-25 12:25:10 -07002493 if (account == null) throw new IllegalArgumentException("account is null");
2494 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002495 int userId = UserHandle.getCallingUserId();
2496 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002497 String msg = String.format(
2498 "uid %s cannot set auth tokens associated with accounts of type: %s",
2499 callingUid,
2500 account.type);
2501 throw new SecurityException(msg);
2502 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002503 long identityToken = clearCallingIdentity();
2504 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002505 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002506 saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002507 } finally {
2508 restoreCallingIdentity(identityToken);
2509 }
Fred Quintana60307342009-03-24 22:48:12 -07002510 }
2511
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002512 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002513 public void setPassword(Account account, String password) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002514 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002515 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2516 Log.v(TAG, "setAuthToken: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002517 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002518 + ", pid " + Binder.getCallingPid());
2519 }
Fred Quintana382601f2010-03-25 12:25:10 -07002520 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002521 int userId = UserHandle.getCallingUserId();
2522 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002523 String msg = String.format(
2524 "uid %s cannot set secrets for accounts of type: %s",
2525 callingUid,
2526 account.type);
2527 throw new SecurityException(msg);
2528 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002529 long identityToken = clearCallingIdentity();
2530 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002531 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002532 setPasswordInternal(accounts, account, password, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002533 } finally {
2534 restoreCallingIdentity(identityToken);
2535 }
Fred Quintana60307342009-03-24 22:48:12 -07002536 }
2537
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002538 private void setPasswordInternal(UserAccounts accounts, Account account, String password,
2539 int callingUid) {
Fred Quintana31957f12009-10-21 13:43:10 -07002540 if (account == null) {
2541 return;
2542 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002543 boolean isChanged = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08002544 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07002545 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002546 db.beginTransaction();
2547 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002548 final long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002549 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002550 AccountsDbUtils.updateAccountPassword(db, accountId, password);
2551 AccountsDbUtils.deleteAuthTokensByAccountId(db, accountId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002552 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07002553 accounts.accountTokenCaches.remove(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002554 db.setTransactionSuccessful();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002555 // If there is an account whose password will be updated and the database
2556 // transactions succeed, then we say that a change has occured. Even if the
2557 // new password is the same as the old and there were no authtokens to delete.
2558 isChanged = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002559 String action = (password == null || password.length() == 0) ?
2560 DebugDbHelper.ACTION_CLEAR_PASSWORD
2561 : DebugDbHelper.ACTION_SET_PASSWORD;
2562 logRecord(db, action, TABLE_ACCOUNTS, accountId, accounts, callingUid);
Costin Manolachef5ffe892011-01-19 09:35:32 -08002563 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08002564 } finally {
2565 db.endTransaction();
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07002566 if (isChanged) {
2567 // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
2568 sendAccountsChangedBroadcast(accounts.userId);
2569 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002570 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08002571 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07002572 }
2573
Amith Yamasani04e0d262012-02-14 11:50:53 -08002574 private void sendAccountsChangedBroadcast(int userId) {
Fred Quintana56285a62010-12-02 14:20:51 -08002575 Log.i(TAG, "the accounts changed, sending broadcast of "
2576 + ACCOUNTS_CHANGED_INTENT.getAction());
Dianne Hackborn79af1dd2012-08-16 16:42:52 -07002577 mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
Fred Quintana33269202009-04-20 16:05:10 -07002578 }
2579
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002580 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002581 public void clearPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002582 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002583 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2584 Log.v(TAG, "clearPassword: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002585 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002586 + ", pid " + Binder.getCallingPid());
2587 }
Fred Quintana382601f2010-03-25 12:25:10 -07002588 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002589 int userId = UserHandle.getCallingUserId();
2590 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002591 String msg = String.format(
2592 "uid %s cannot clear passwords for accounts of type: %s",
2593 callingUid,
2594 account.type);
2595 throw new SecurityException(msg);
2596 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002597 long identityToken = clearCallingIdentity();
2598 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002599 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07002600 setPasswordInternal(accounts, account, null, callingUid);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002601 } finally {
2602 restoreCallingIdentity(identityToken);
2603 }
Fred Quintana60307342009-03-24 22:48:12 -07002604 }
2605
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002606 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07002607 public void setUserData(Account account, String key, String value) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002608 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08002609 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2610 Log.v(TAG, "setUserData: " + account
2611 + ", key " + key
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002612 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08002613 + ", pid " + Binder.getCallingPid());
2614 }
Fred Quintana382601f2010-03-25 12:25:10 -07002615 if (key == null) throw new IllegalArgumentException("key is null");
2616 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002617 int userId = UserHandle.getCallingUserId();
2618 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07002619 String msg = String.format(
2620 "uid %s cannot set user data for accounts of type: %s",
2621 callingUid,
2622 account.type);
2623 throw new SecurityException(msg);
2624 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002625 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07002626 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002627 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002628 synchronized (accounts.cacheLock) {
2629 if (!accountExistsCacheLocked(accounts, account)) {
2630 return;
2631 }
2632 setUserdataInternalLocked(accounts, account, key, value);
2633 }
Fred Quintana60307342009-03-24 22:48:12 -07002634 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002635 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07002636 }
2637 }
2638
Simranjit Kohli858511c2016-03-10 18:36:11 +00002639 private boolean accountExistsCacheLocked(UserAccounts accounts, Account account) {
2640 if (accounts.accountCache.containsKey(account.type)) {
2641 for (Account acc : accounts.accountCache.get(account.type)) {
2642 if (acc.name.equals(account.name)) {
2643 return true;
2644 }
2645 }
2646 }
2647 return false;
2648 }
2649
2650 private void setUserdataInternalLocked(UserAccounts accounts, Account account, String key,
Amith Yamasani04e0d262012-02-14 11:50:53 -08002651 String value) {
Fred Quintana31957f12009-10-21 13:43:10 -07002652 if (account == null || key == null) {
2653 return;
2654 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00002655 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
2656 db.beginTransaction();
2657 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002658 long accountId = AccountsDbUtils.findAccountId(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002659 if (accountId < 0) {
2660 return;
2661 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002662 long extrasId = AccountsDbUtils.findExtrasIdByAccountId(db, accountId, key);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002663 if (extrasId < 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002664 extrasId = AccountsDbUtils.insertExtra(db, accountId, key, value);
Simranjit Kohli858511c2016-03-10 18:36:11 +00002665 if (extrasId < 0) {
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002666 return;
2667 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07002668 } else if (!AccountsDbUtils.updateExtra(db, extrasId, value)) {
2669 return;
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002670 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00002671 writeUserDataIntoCacheLocked(accounts, db, account, key, value);
2672 db.setTransactionSuccessful();
2673 } finally {
2674 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07002675 }
2676 }
2677
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002678 private void onResult(IAccountManagerResponse response, Bundle result) {
Fred Quintana56285a62010-12-02 14:20:51 -08002679 if (result == null) {
2680 Log.e(TAG, "the result is unexpectedly null", new Exception());
2681 }
2682 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2683 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
2684 + response);
2685 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002686 try {
2687 response.onResult(result);
2688 } catch (RemoteException e) {
2689 // if the caller is dead then there is no one to care about remote
2690 // exceptions
2691 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2692 Log.v(TAG, "failure while notifying response", e);
2693 }
2694 }
2695 }
2696
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002697 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07002698 public void getAuthTokenLabel(IAccountManagerResponse response, final String accountType,
2699 final String authTokenType)
2700 throws RemoteException {
2701 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolache5f383ad92010-12-02 16:44:46 -08002702 if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
2703
Fred Quintanad9640ec2012-05-23 12:37:00 -07002704 final int callingUid = getCallingUid();
2705 clearCallingIdentity();
Amith Yamasani27db4682013-03-30 17:07:47 -07002706 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07002707 throw new SecurityException("can only call from system");
2708 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002709 int userId = UserHandle.getUserId(callingUid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002710 long identityToken = clearCallingIdentity();
2711 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002712 UserAccounts accounts = getUserAccounts(userId);
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002713 new Session(accounts, response, accountType, false /* expectActivityLaunch */,
2714 false /* stripAuthTokenFromResult */, null /* accountName */,
2715 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002716 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002717 protected String toDebugString(long now) {
2718 return super.toDebugString(now) + ", getAuthTokenLabel"
Fred Quintanad9640ec2012-05-23 12:37:00 -07002719 + ", " + accountType
Costin Manolache5f383ad92010-12-02 16:44:46 -08002720 + ", authTokenType " + authTokenType;
2721 }
2722
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002723 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002724 public void run() throws RemoteException {
2725 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2726 }
2727
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002728 @Override
Costin Manolache5f383ad92010-12-02 16:44:46 -08002729 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002730 Bundle.setDefusable(result, true);
Costin Manolache5f383ad92010-12-02 16:44:46 -08002731 if (result != null) {
2732 String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
2733 Bundle bundle = new Bundle();
2734 bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, label);
2735 super.onResult(bundle);
2736 return;
2737 } else {
2738 super.onResult(result);
2739 }
2740 }
2741 }.bind();
2742 } finally {
2743 restoreCallingIdentity(identityToken);
2744 }
2745 }
2746
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002747 @Override
Carlos Valdivia91979be2015-05-22 14:11:35 -07002748 public void getAuthToken(
2749 IAccountManagerResponse response,
2750 final Account account,
2751 final String authTokenType,
2752 final boolean notifyOnAuthFailure,
2753 final boolean expectActivityLaunch,
2754 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002755 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08002756 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2757 Log.v(TAG, "getAuthToken: " + account
2758 + ", response " + response
2759 + ", authTokenType " + authTokenType
2760 + ", notifyOnAuthFailure " + notifyOnAuthFailure
2761 + ", expectActivityLaunch " + expectActivityLaunch
2762 + ", caller's uid " + Binder.getCallingUid()
2763 + ", pid " + Binder.getCallingPid());
2764 }
Fred Quintana382601f2010-03-25 12:25:10 -07002765 if (response == null) throw new IllegalArgumentException("response is null");
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08002766 try {
2767 if (account == null) {
2768 Slog.w(TAG, "getAuthToken called with null account");
2769 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "account is null");
2770 return;
2771 }
2772 if (authTokenType == null) {
2773 Slog.w(TAG, "getAuthToken called with null authTokenType");
2774 response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
2775 return;
2776 }
2777 } catch (RemoteException e) {
2778 Slog.w(TAG, "Failed to report error back to the client." + e);
2779 return;
2780 }
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002781 int userId = UserHandle.getCallingUserId();
2782 long ident = Binder.clearCallingIdentity();
2783 final UserAccounts accounts;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002784 final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002785 try {
2786 accounts = getUserAccounts(userId);
2787 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
2788 AuthenticatorDescription.newKey(account.type), accounts.userId);
2789 } finally {
2790 Binder.restoreCallingIdentity(ident);
2791 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002792
Costin Manolachea40c6302010-12-13 14:50:45 -08002793 final boolean customTokens =
Carlos Valdivia91979be2015-05-22 14:11:35 -07002794 authenticatorInfo != null && authenticatorInfo.type.customTokens;
Costin Manolachea40c6302010-12-13 14:50:45 -08002795
2796 // skip the check if customTokens
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002797 final int callerUid = Binder.getCallingUid();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00002798 final boolean permissionGranted =
2799 customTokens || permissionIsGranted(account, authTokenType, callerUid, userId);
Costin Manolachea40c6302010-12-13 14:50:45 -08002800
Carlos Valdivia91979be2015-05-22 14:11:35 -07002801 // Get the calling package. We will use it for the purpose of caching.
2802 final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Amith Yamasanie7360012015-06-03 17:39:40 -07002803 List<String> callerOwnedPackageNames;
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07002804 ident = Binder.clearCallingIdentity();
Amith Yamasanie7360012015-06-03 17:39:40 -07002805 try {
2806 callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
2807 } finally {
2808 Binder.restoreCallingIdentity(ident);
2809 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002810 if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
2811 String msg = String.format(
2812 "Uid %s is attempting to illegally masquerade as package %s!",
2813 callerUid,
2814 callerPkg);
2815 throw new SecurityException(msg);
2816 }
2817
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002818 // let authenticator know the identity of the caller
2819 loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
2820 loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
Carlos Valdivia91979be2015-05-22 14:11:35 -07002821
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07002822 if (notifyOnAuthFailure) {
2823 loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
Costin Manolachea40c6302010-12-13 14:50:45 -08002824 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07002825
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002826 long identityToken = clearCallingIdentity();
2827 try {
Amith Yamasanie7360012015-06-03 17:39:40 -07002828 // Distill the caller's package signatures into a single digest.
2829 final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
2830
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002831 // if the caller has permission, do the peek. otherwise go the more expensive
2832 // route of starting a Session
Costin Manolachea40c6302010-12-13 14:50:45 -08002833 if (!customTokens && permissionGranted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08002834 String authToken = readAuthTokenInternal(accounts, account, authTokenType);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002835 if (authToken != null) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002836 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002837 result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
2838 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2839 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002840 onResult(response, result);
2841 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07002842 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002843 }
2844
Carlos Valdivia91979be2015-05-22 14:11:35 -07002845 if (customTokens) {
2846 /*
2847 * Look up tokens in the new cache only if the loginOptions don't have parameters
2848 * outside of those expected to be injected by the AccountManager, e.g.
2849 * ANDORID_PACKAGE_NAME.
2850 */
2851 String token = readCachedTokenInternal(
2852 accounts,
2853 account,
2854 authTokenType,
2855 callerPkg,
2856 callerPkgSigDigest);
2857 if (token != null) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002858 if (Log.isLoggable(TAG, Log.VERBOSE)) {
2859 Log.v(TAG, "getAuthToken: cache hit ofr custom token authenticator.");
2860 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002861 Bundle result = new Bundle();
2862 result.putString(AccountManager.KEY_AUTHTOKEN, token);
2863 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
2864 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
2865 onResult(response, result);
2866 return;
2867 }
2868 }
2869
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002870 new Session(
2871 accounts,
2872 response,
2873 account.type,
2874 expectActivityLaunch,
2875 false /* stripAuthTokenFromResult */,
2876 account.name,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08002877 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002878 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002879 protected String toDebugString(long now) {
2880 if (loginOptions != null) loginOptions.keySet();
2881 return super.toDebugString(now) + ", getAuthToken"
2882 + ", " + account
2883 + ", authTokenType " + authTokenType
2884 + ", loginOptions " + loginOptions
2885 + ", notifyOnAuthFailure " + notifyOnAuthFailure;
2886 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002887
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002888 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002889 public void run() throws RemoteException {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002890 // If the caller doesn't have permission then create and return the
2891 // "grant permission" intent instead of the "getAuthToken" intent.
2892 if (!permissionGranted) {
2893 mAuthenticator.getAuthTokenLabel(this, authTokenType);
2894 } else {
2895 mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
2896 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002897 }
2898
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07002899 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002900 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06002901 Bundle.setDefusable(result, true);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002902 if (result != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002903 if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002904 Intent intent = newGrantCredentialsPermissionIntent(
2905 account,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002906 null,
Carlos Valdiviac37ee222015-06-17 20:17:37 -07002907 callerUid,
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002908 new AccountAuthenticatorResponse(this),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002909 authTokenType,
2910 true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002911 Bundle bundle = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002912 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002913 onResult(bundle);
2914 return;
2915 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002916 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002917 if (authToken != null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002918 String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
2919 String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002920 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002921 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002922 "the type and name should not be empty");
2923 return;
2924 }
Carlos Valdivia91979be2015-05-22 14:11:35 -07002925 Account resultAccount = new Account(name, type);
Costin Manolachea40c6302010-12-13 14:50:45 -08002926 if (!customTokens) {
Carlos Valdivia91979be2015-05-22 14:11:35 -07002927 saveAuthTokenToDatabase(
2928 mAccounts,
2929 resultAccount,
2930 authTokenType,
2931 authToken);
2932 }
2933 long expiryMillis = result.getLong(
2934 AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
2935 if (customTokens
2936 && expiryMillis > System.currentTimeMillis()) {
2937 saveCachedToken(
2938 mAccounts,
2939 account,
2940 callerPkg,
2941 callerPkgSigDigest,
2942 authTokenType,
2943 authToken,
2944 expiryMillis);
Costin Manolachea40c6302010-12-13 14:50:45 -08002945 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002946 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002947
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002948 Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
Costin Manolached6060452011-01-24 16:11:36 -08002949 if (intent != null && notifyOnAuthFailure && !customTokens) {
Carlos Valdivia06329e5f2016-05-07 21:46:15 -07002950 /*
2951 * Make sure that the supplied intent is owned by the authenticator
2952 * giving it to the system. Otherwise a malicious authenticator could
2953 * have users launching arbitrary activities by tricking users to
2954 * interact with malicious notifications.
2955 */
2956 checkKeyIntent(
2957 Binder.getCallingUid(),
2958 intent);
Amith Yamasani04e0d262012-02-14 11:50:53 -08002959 doNotification(mAccounts,
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07002960 account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002961 intent, "android", accounts.userId);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002962 }
Fred Quintanaa698f422009-04-08 19:14:54 -07002963 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002964 super.onResult(result);
Fred Quintanaa698f422009-04-08 19:14:54 -07002965 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07002966 }.bind();
2967 } finally {
2968 restoreCallingIdentity(identityToken);
2969 }
Fred Quintana60307342009-03-24 22:48:12 -07002970 }
2971
Carlos Valdivia91979be2015-05-22 14:11:35 -07002972 private byte[] calculatePackageSignatureDigest(String callerPkg) {
2973 MessageDigest digester;
2974 try {
2975 digester = MessageDigest.getInstance("SHA-256");
2976 PackageInfo pkgInfo = mPackageManager.getPackageInfo(
2977 callerPkg, PackageManager.GET_SIGNATURES);
2978 for (Signature sig : pkgInfo.signatures) {
2979 digester.update(sig.toByteArray());
2980 }
2981 } catch (NoSuchAlgorithmException x) {
2982 Log.wtf(TAG, "SHA-256 should be available", x);
2983 digester = null;
2984 } catch (NameNotFoundException e) {
2985 Log.w(TAG, "Could not find packageinfo for: " + callerPkg);
2986 digester = null;
2987 }
2988 return (digester == null) ? null : digester.digest();
2989 }
2990
Dianne Hackborn41203752012-08-31 14:05:51 -07002991 private void createNoCredentialsPermissionNotification(Account account, Intent intent,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002992 String packageName, int userId) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07002993 int uid = intent.getIntExtra(
2994 GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
2995 String authTokenType = intent.getStringExtra(
2996 GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
Eric Fischeree452ee2009-08-31 17:58:06 -07002997 final String titleAndSubtitle =
2998 mContext.getString(R.string.permission_request_notification_with_subtitle,
2999 account.name);
3000 final int index = titleAndSubtitle.indexOf('\n');
Costin Manolache85e72792011-10-07 09:42:49 -07003001 String title = titleAndSubtitle;
3002 String subtitle = "";
3003 if (index > 0) {
3004 title = titleAndSubtitle.substring(0, index);
Maggie Benthalla12fccf2013-03-14 18:02:12 -04003005 subtitle = titleAndSubtitle.substring(index + 1);
Costin Manolache85e72792011-10-07 09:42:49 -07003006 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07003007 UserHandle user = UserHandle.of(userId);
Kenny Guy07ad8dc2014-09-01 20:56:12 +01003008 Context contextForUser = getContextForUser(user);
Chris Wren1ce4b6d2015-06-11 10:19:43 -04003009 Notification n = new Notification.Builder(contextForUser)
3010 .setSmallIcon(android.R.drawable.stat_sys_warning)
3011 .setWhen(0)
3012 .setColor(contextForUser.getColor(
3013 com.android.internal.R.color.system_notification_accent_color))
3014 .setContentTitle(title)
3015 .setContentText(subtitle)
3016 .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0, intent,
3017 PendingIntent.FLAG_CANCEL_CURRENT, null, user))
3018 .build();
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07003019 installNotification(getCredentialPermissionNotificationId(
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003020 account, authTokenType, uid), n, packageName, user.getIdentifier());
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003021 }
3022
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003023 private Intent newGrantCredentialsPermissionIntent(Account account, String packageName,
3024 int uid, AccountAuthenticatorResponse response, String authTokenType,
3025 boolean startInNewTask) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003026
3027 Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
Costin Manolache5f383ad92010-12-02 16:44:46 -08003028
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003029 if (startInNewTask) {
3030 // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag.
3031 // Since it was set in Eclair+ we can't change it without breaking apps using
3032 // the intent from a non-Activity context. This is the default behavior.
3033 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3034 }
3035 intent.addCategory(String.valueOf(getCredentialPermissionNotificationId(account,
3036 authTokenType, uid) + (packageName != null ? packageName : "")));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003037 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003038 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
3039 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003040 intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
Costin Manolache5f383ad92010-12-02 16:44:46 -08003041
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003042 return intent;
3043 }
3044
3045 private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
3046 int uid) {
3047 Integer id;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07003048 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08003049 synchronized (accounts.credentialsPermissionNotificationIds) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003050 final Pair<Pair<Account, String>, Integer> key =
3051 new Pair<Pair<Account, String>, Integer>(
3052 new Pair<Account, String>(account, authTokenType), uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003053 id = accounts.credentialsPermissionNotificationIds.get(key);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003054 if (id == null) {
3055 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08003056 accounts.credentialsPermissionNotificationIds.put(key, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003057 }
3058 }
3059 return id;
3060 }
3061
Amith Yamasani04e0d262012-02-14 11:50:53 -08003062 private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003063 Integer id;
Amith Yamasani04e0d262012-02-14 11:50:53 -08003064 synchronized (accounts.signinRequiredNotificationIds) {
3065 id = accounts.signinRequiredNotificationIds.get(account);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003066 if (id == null) {
3067 id = mNotificationIds.incrementAndGet();
Amith Yamasani04e0d262012-02-14 11:50:53 -08003068 accounts.signinRequiredNotificationIds.put(account, id);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07003069 }
3070 }
3071 return id;
3072 }
3073
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003074 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07003075 public void addAccount(final IAccountManagerResponse response, final String accountType,
Fred Quintana33269202009-04-20 16:05:10 -07003076 final String authTokenType, final String[] requiredFeatures,
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003077 final boolean expectActivityLaunch, final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003078 Bundle.setDefusable(optionsIn, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003079 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3080 Log.v(TAG, "addAccount: accountType " + accountType
3081 + ", response " + response
3082 + ", authTokenType " + authTokenType
3083 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3084 + ", expectActivityLaunch " + expectActivityLaunch
3085 + ", caller's uid " + Binder.getCallingUid()
3086 + ", pid " + Binder.getCallingPid());
3087 }
Fred Quintana382601f2010-03-25 12:25:10 -07003088 if (response == null) throw new IllegalArgumentException("response is null");
3089 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003090
Amith Yamasani71e6c692013-03-24 17:39:28 -07003091 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003092 final int uid = Binder.getCallingUid();
3093 final int userId = UserHandle.getUserId(uid);
3094 if (!canUserModifyAccounts(userId, uid)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003095 try {
3096 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3097 "User is not allowed to add an account!");
3098 } catch (RemoteException re) {
3099 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003100 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003101 return;
3102 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003103 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07003104 try {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003105 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3106 "User cannot modify accounts of this type (policy).");
3107 } catch (RemoteException re) {
Amith Yamasani23c8b962013-04-10 13:37:18 -07003108 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003109 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3110 userId);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08003111 return;
3112 }
3113
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003114 final int pid = Binder.getCallingPid();
Costin Manolacheb61e8fb2011-09-08 11:26:09 -07003115 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3116 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3117 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3118
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003119 int usrId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003120 long identityToken = clearCallingIdentity();
3121 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003122 UserAccounts accounts = getUserAccounts(usrId);
3123 logRecordWithUid(
3124 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, uid);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003125 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003126 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003127 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003128 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003129 public void run() throws RemoteException {
Costin Manolache3348f142009-09-29 18:58:36 -07003130 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
Fred Quintana33269202009-04-20 16:05:10 -07003131 options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003132 }
Fred Quintanaa698f422009-04-08 19:14:54 -07003133
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003134 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003135 protected String toDebugString(long now) {
3136 return super.toDebugString(now) + ", addAccount"
Fred Quintana33269202009-04-20 16:05:10 -07003137 + ", accountType " + accountType
3138 + ", requiredFeatures "
3139 + (requiredFeatures != null
3140 ? TextUtils.join(",", requiredFeatures)
3141 : null);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003142 }
3143 }.bind();
3144 } finally {
3145 restoreCallingIdentity(identityToken);
3146 }
Fred Quintana60307342009-03-24 22:48:12 -07003147 }
3148
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003149 @Override
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003150 public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
3151 final String authTokenType, final String[] requiredFeatures,
3152 final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003153 Bundle.setDefusable(optionsIn, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003154 int callingUid = Binder.getCallingUid();
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003155 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3156 Log.v(TAG, "addAccount: accountType " + accountType
3157 + ", response " + response
3158 + ", authTokenType " + authTokenType
3159 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3160 + ", expectActivityLaunch " + expectActivityLaunch
3161 + ", caller's uid " + Binder.getCallingUid()
3162 + ", pid " + Binder.getCallingPid()
3163 + ", for user id " + userId);
3164 }
3165 if (response == null) throw new IllegalArgumentException("response is null");
3166 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003167 // Only allow the system process to add accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003168 if (isCrossUser(callingUid, userId)) {
3169 throw new SecurityException(
3170 String.format(
3171 "User %s trying to add account for %s" ,
3172 UserHandle.getCallingUserId(),
3173 userId));
3174 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003175
3176 // Is user disallowed from modifying accounts?
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003177 if (!canUserModifyAccounts(userId, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003178 try {
3179 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3180 "User is not allowed to add an account!");
3181 } catch (RemoteException re) {
3182 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003183 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003184 return;
3185 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003186 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003187 try {
3188 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3189 "User cannot modify accounts of this type (policy).");
3190 } catch (RemoteException re) {
3191 }
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003192 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3193 userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003194 return;
3195 }
3196
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003197 final int pid = Binder.getCallingPid();
3198 final int uid = Binder.getCallingUid();
3199 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3200 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3201 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3202
3203 long identityToken = clearCallingIdentity();
3204 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003205 UserAccounts accounts = getUserAccounts(userId);
3206 logRecordWithUid(
3207 accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_ADD, TABLE_ACCOUNTS, userId);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003208 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003209 true /* stripAuthTokenFromResult */, null /* accountName */,
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07003210 false /* authDetailsRequired */, true /* updateLastAuthenticationTime */) {
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003211 @Override
3212 public void run() throws RemoteException {
3213 mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
3214 options);
3215 }
3216
3217 @Override
3218 protected String toDebugString(long now) {
3219 return super.toDebugString(now) + ", addAccount"
3220 + ", accountType " + accountType
3221 + ", requiredFeatures "
3222 + (requiredFeatures != null
3223 ? TextUtils.join(",", requiredFeatures)
3224 : null);
3225 }
3226 }.bind();
3227 } finally {
3228 restoreCallingIdentity(identityToken);
3229 }
3230 }
3231
Sandra Kwan78812282015-11-04 11:19:47 -08003232 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003233 public void startAddAccountSession(
3234 final IAccountManagerResponse response,
3235 final String accountType,
3236 final String authTokenType,
3237 final String[] requiredFeatures,
Sandra Kwan78812282015-11-04 11:19:47 -08003238 final boolean expectActivityLaunch,
3239 final Bundle optionsIn) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003240 Bundle.setDefusable(optionsIn, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003241 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3242 Log.v(TAG,
3243 "startAddAccountSession: accountType " + accountType
3244 + ", response " + response
3245 + ", authTokenType " + authTokenType
3246 + ", requiredFeatures " + stringArrayToString(requiredFeatures)
3247 + ", expectActivityLaunch " + expectActivityLaunch
3248 + ", caller's uid " + Binder.getCallingUid()
3249 + ", pid " + Binder.getCallingPid());
3250 }
3251 if (response == null) {
3252 throw new IllegalArgumentException("response is null");
3253 }
3254 if (accountType == null) {
3255 throw new IllegalArgumentException("accountType is null");
3256 }
3257
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003258 final int uid = Binder.getCallingUid();
3259 final int userId = UserHandle.getUserId(uid);
3260 if (!canUserModifyAccounts(userId, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003261 try {
3262 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
3263 "User is not allowed to add an account!");
3264 } catch (RemoteException re) {
3265 }
3266 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3267 return;
3268 }
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00003269 if (!canUserModifyAccountsForType(userId, accountType, uid)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003270 try {
3271 response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3272 "User cannot modify accounts of this type (policy).");
3273 } catch (RemoteException re) {
3274 }
3275 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3276 userId);
3277 return;
3278 }
Sandra Kwan78812282015-11-04 11:19:47 -08003279 final int pid = Binder.getCallingPid();
Sandra Kwan78812282015-11-04 11:19:47 -08003280 final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
3281 options.putInt(AccountManager.KEY_CALLER_UID, uid);
3282 options.putInt(AccountManager.KEY_CALLER_PID, pid);
3283
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003284 // Check to see if the Password should be included to the caller.
3285 String callerPkg = optionsIn.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3286 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003287 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003288
Sandra Kwan78812282015-11-04 11:19:47 -08003289 long identityToken = clearCallingIdentity();
3290 try {
Hongming Jin368aa192016-07-29 14:29:54 -07003291 UserAccounts accounts = getUserAccounts(userId);
Sandra Kwan78812282015-11-04 11:19:47 -08003292 logRecordWithUid(accounts, DebugDbHelper.ACTION_CALLED_START_ACCOUNT_ADD,
3293 TABLE_ACCOUNTS, uid);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003294 new StartAccountSession(
3295 accounts,
3296 response,
3297 accountType,
3298 expectActivityLaunch,
3299 null /* accountName */,
3300 false /* authDetailsRequired */,
3301 true /* updateLastAuthenticationTime */,
3302 isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003303 @Override
3304 public void run() throws RemoteException {
3305 mAuthenticator.startAddAccountSession(this, mAccountType, authTokenType,
3306 requiredFeatures, options);
3307 }
3308
3309 @Override
3310 protected String toDebugString(long now) {
3311 String requiredFeaturesStr = TextUtils.join(",", requiredFeatures);
3312 return super.toDebugString(now) + ", startAddAccountSession" + ", accountType "
3313 + accountType + ", requiredFeatures "
3314 + (requiredFeatures != null ? requiredFeaturesStr : null);
3315 }
3316 }.bind();
3317 } finally {
3318 restoreCallingIdentity(identityToken);
3319 }
3320 }
3321
3322 /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
3323 private abstract class StartAccountSession extends Session {
3324
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003325 private final boolean mIsPasswordForwardingAllowed;
3326
3327 public StartAccountSession(
3328 UserAccounts accounts,
3329 IAccountManagerResponse response,
3330 String accountType,
3331 boolean expectActivityLaunch,
3332 String accountName,
3333 boolean authDetailsRequired,
3334 boolean updateLastAuthenticationTime,
3335 boolean isPasswordForwardingAllowed) {
Sandra Kwan78812282015-11-04 11:19:47 -08003336 super(accounts, response, accountType, expectActivityLaunch,
3337 true /* stripAuthTokenFromResult */, accountName, authDetailsRequired,
3338 updateLastAuthenticationTime);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003339 mIsPasswordForwardingAllowed = isPasswordForwardingAllowed;
Sandra Kwan78812282015-11-04 11:19:47 -08003340 }
3341
3342 @Override
3343 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003344 Bundle.setDefusable(result, true);
Sandra Kwan78812282015-11-04 11:19:47 -08003345 mNumResults++;
3346 Intent intent = null;
Sandra Kwan78812282015-11-04 11:19:47 -08003347 if (result != null
3348 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08003349 checkKeyIntent(
3350 Binder.getCallingUid(),
3351 intent);
Sandra Kwan78812282015-11-04 11:19:47 -08003352 }
Sandra Kwan78812282015-11-04 11:19:47 -08003353 IAccountManagerResponse response;
3354 if (mExpectActivityLaunch && result != null
3355 && result.containsKey(AccountManager.KEY_INTENT)) {
3356 response = mResponse;
3357 } else {
3358 response = getResponseAndClose();
3359 }
3360 if (response == null) {
3361 return;
3362 }
3363 if (result == null) {
3364 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3365 Log.v(TAG, getClass().getSimpleName() + " calling onError() on response "
3366 + response);
3367 }
3368 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3369 "null bundle returned");
3370 return;
3371 }
3372
3373 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) && (intent == null)) {
3374 // All AccountManager error codes are greater
3375 // than 0
3376 sendErrorResponse(response, result.getInt(AccountManager.KEY_ERROR_CODE),
3377 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3378 return;
3379 }
3380
Hongming Jin368aa192016-07-29 14:29:54 -07003381 // Omit passwords if the caller isn't permitted to see them.
3382 if (!mIsPasswordForwardingAllowed) {
3383 result.remove(AccountManager.KEY_PASSWORD);
3384 }
3385
Sandra Kwan78812282015-11-04 11:19:47 -08003386 // Strip auth token from result.
3387 result.remove(AccountManager.KEY_AUTHTOKEN);
3388
3389 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3390 Log.v(TAG,
3391 getClass().getSimpleName() + " calling onResult() on response " + response);
3392 }
3393
3394 // Get the session bundle created by authenticator. The
3395 // bundle contains data necessary for finishing the session
3396 // later. The session bundle will be encrypted here and
3397 // decrypted later when trying to finish the session.
3398 Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
3399 if (sessionBundle != null) {
3400 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3401 if (TextUtils.isEmpty(accountType)
Andreas Gampe9b041742015-12-11 17:23:33 -08003402 || !mAccountType.equalsIgnoreCase(accountType)) {
Sandra Kwan78812282015-11-04 11:19:47 -08003403 Log.w(TAG, "Account type in session bundle doesn't match request.");
3404 }
3405 // Add accountType info to session bundle. This will
3406 // override any value set by authenticator.
3407 sessionBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccountType);
3408
3409 // Encrypt session bundle before returning to caller.
3410 try {
3411 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3412 Bundle encryptedBundle = cryptoHelper.encryptBundle(sessionBundle);
3413 result.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, encryptedBundle);
3414 } catch (GeneralSecurityException e) {
3415 if (Log.isLoggable(TAG, Log.DEBUG)) {
3416 Log.v(TAG, "Failed to encrypt session bundle!", e);
3417 }
3418 sendErrorResponse(response, AccountManager.ERROR_CODE_INVALID_RESPONSE,
3419 "failed to encrypt session bundle");
3420 return;
3421 }
3422 }
3423
3424 sendResponse(response, result);
3425 }
3426 }
3427
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003428 @Override
Sandra Kwan0b84b452016-01-20 15:25:42 -08003429 public void finishSessionAsUser(IAccountManagerResponse response,
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003430 @NonNull Bundle sessionBundle,
3431 boolean expectActivityLaunch,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003432 Bundle appInfo,
3433 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003434 Bundle.setDefusable(sessionBundle, true);
Sandra Kwan0b84b452016-01-20 15:25:42 -08003435 int callingUid = Binder.getCallingUid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003436 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3437 Log.v(TAG,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003438 "finishSession: response "+ response
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003439 + ", expectActivityLaunch " + expectActivityLaunch
Sandra Kwan0b84b452016-01-20 15:25:42 -08003440 + ", caller's uid " + callingUid
3441 + ", caller's user id " + UserHandle.getCallingUserId()
3442 + ", pid " + Binder.getCallingPid()
3443 + ", for user id " + userId);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003444 }
3445 if (response == null) {
3446 throw new IllegalArgumentException("response is null");
3447 }
3448
3449 // Session bundle is the encrypted bundle of the original bundle created by authenticator.
3450 // Account type is added to it before encryption.
3451 if (sessionBundle == null || sessionBundle.size() == 0) {
3452 throw new IllegalArgumentException("sessionBundle is empty");
3453 }
3454
Sandra Kwan0b84b452016-01-20 15:25:42 -08003455 // Only allow the system process to finish session for other users
3456 if (isCrossUser(callingUid, userId)) {
3457 throw new SecurityException(
3458 String.format(
3459 "User %s trying to finish session for %s without cross user permission",
3460 UserHandle.getCallingUserId(),
3461 userId));
3462 }
3463
Sandra Kwan0b84b452016-01-20 15:25:42 -08003464 if (!canUserModifyAccounts(userId, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003465 sendErrorResponse(response,
3466 AccountManager.ERROR_CODE_USER_RESTRICTED,
3467 "User is not allowed to add an account!");
3468 showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED, userId);
3469 return;
3470 }
3471
3472 final int pid = Binder.getCallingPid();
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003473 final Bundle decryptedBundle;
3474 final String accountType;
3475 // First decrypt session bundle to get account type for checking permission.
3476 try {
3477 CryptoHelper cryptoHelper = CryptoHelper.getInstance();
3478 decryptedBundle = cryptoHelper.decryptBundle(sessionBundle);
3479 if (decryptedBundle == null) {
3480 sendErrorResponse(
3481 response,
3482 AccountManager.ERROR_CODE_BAD_REQUEST,
3483 "failed to decrypt session bundle");
3484 return;
3485 }
3486 accountType = decryptedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
3487 // Account type cannot be null. This should not happen if session bundle was created
3488 // properly by #StartAccountSession.
3489 if (TextUtils.isEmpty(accountType)) {
3490 sendErrorResponse(
3491 response,
3492 AccountManager.ERROR_CODE_BAD_ARGUMENTS,
3493 "accountType is empty");
3494 return;
3495 }
3496
3497 // If by any chances, decryptedBundle contains colliding keys with
3498 // system info
3499 // such as AccountManager.KEY_ANDROID_PACKAGE_NAME required by the add account flow or
3500 // update credentials flow, we should replace with the new values of the current call.
3501 if (appInfo != null) {
3502 decryptedBundle.putAll(appInfo);
3503 }
3504
3505 // Add info that may be used by add account or update credentials flow.
Sandra Kwan0b84b452016-01-20 15:25:42 -08003506 decryptedBundle.putInt(AccountManager.KEY_CALLER_UID, callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003507 decryptedBundle.putInt(AccountManager.KEY_CALLER_PID, pid);
3508 } catch (GeneralSecurityException e) {
3509 if (Log.isLoggable(TAG, Log.DEBUG)) {
3510 Log.v(TAG, "Failed to decrypt session bundle!", e);
3511 }
3512 sendErrorResponse(
3513 response,
3514 AccountManager.ERROR_CODE_BAD_REQUEST,
3515 "failed to decrypt session bundle");
3516 return;
3517 }
3518
Sandra Kwan0b84b452016-01-20 15:25:42 -08003519 if (!canUserModifyAccountsForType(userId, accountType, callingUid)) {
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003520 sendErrorResponse(
3521 response,
3522 AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3523 "User cannot modify accounts of this type (policy).");
3524 showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
3525 userId);
3526 return;
3527 }
3528
3529 long identityToken = clearCallingIdentity();
3530 try {
3531 UserAccounts accounts = getUserAccounts(userId);
3532 logRecordWithUid(
3533 accounts,
3534 DebugDbHelper.ACTION_CALLED_ACCOUNT_SESSION_FINISH,
3535 TABLE_ACCOUNTS,
Sandra Kwan0b84b452016-01-20 15:25:42 -08003536 callingUid);
Sandra Kwan920f6ef2015-11-10 14:13:29 -08003537 new Session(
3538 accounts,
3539 response,
3540 accountType,
3541 expectActivityLaunch,
3542 true /* stripAuthTokenFromResult */,
3543 null /* accountName */,
3544 false /* authDetailsRequired */,
3545 true /* updateLastAuthenticationTime */) {
3546 @Override
3547 public void run() throws RemoteException {
3548 mAuthenticator.finishSession(this, mAccountType, decryptedBundle);
3549 }
3550
3551 @Override
3552 protected String toDebugString(long now) {
3553 return super.toDebugString(now)
3554 + ", finishSession"
3555 + ", accountType " + accountType;
3556 }
3557 }.bind();
3558 } finally {
3559 restoreCallingIdentity(identityToken);
3560 }
3561 }
3562
Amith Yamasaniae7034a2014-09-22 12:42:12 -07003563 private void showCantAddAccount(int errorCode, int userId) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003564 final DevicePolicyManagerInternal dpmi =
3565 LocalServices.getService(DevicePolicyManagerInternal.class);
3566 Intent intent = null;
Nicolas Prevot14fc1972016-08-24 14:21:38 +01003567 if (dpmi == null) {
3568 intent = getDefaultCantAddAccountIntent(errorCode);
3569 } else if (errorCode == AccountManager.ERROR_CODE_USER_RESTRICTED) {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003570 intent = dpmi.createUserRestrictionSupportIntent(userId,
3571 UserManager.DISALLOW_MODIFY_ACCOUNTS);
3572 } else if (errorCode == AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
3573 intent = dpmi.createShowAdminSupportIntent(userId, false);
3574 }
3575 if (intent == null) {
3576 intent = getDefaultCantAddAccountIntent(errorCode);
3577 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003578 long identityToken = clearCallingIdentity();
3579 try {
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003580 mContext.startActivityAsUser(intent, new UserHandle(userId));
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003581 } finally {
3582 restoreCallingIdentity(identityToken);
3583 }
3584 }
3585
Nicolas Prevot709a63d2016-06-09 13:14:00 +01003586 /**
3587 * Called when we don't know precisely who is preventing us from adding an account.
3588 */
3589 private Intent getDefaultCantAddAccountIntent(int errorCode) {
3590 Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
3591 cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
3592 cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
3593 return cantAddAccount;
3594 }
3595
Alexandra Gherghina999d3942014-07-03 11:40:08 +01003596 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003597 public void confirmCredentialsAsUser(
3598 IAccountManagerResponse response,
3599 final Account account,
3600 final Bundle options,
3601 final boolean expectActivityLaunch,
Amith Yamasani2c7bc262012-11-05 16:46:02 -08003602 int userId) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003603 Bundle.setDefusable(options, true);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003604 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003605 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3606 Log.v(TAG, "confirmCredentials: " + account
3607 + ", response " + response
3608 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003609 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003610 + ", pid " + Binder.getCallingPid());
3611 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07003612 // Only allow the system process to read accounts of other users
3613 if (isCrossUser(callingUid, userId)) {
3614 throw new SecurityException(
3615 String.format(
3616 "User %s trying to confirm account credentials for %s" ,
3617 UserHandle.getCallingUserId(),
3618 userId));
3619 }
Fred Quintana382601f2010-03-25 12:25:10 -07003620 if (response == null) throw new IllegalArgumentException("response is null");
3621 if (account == null) throw new IllegalArgumentException("account is null");
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003622 long identityToken = clearCallingIdentity();
3623 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003624 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003625 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003626 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003627 true /* authDetailsRequired */, true /* updateLastAuthenticatedTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003628 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003629 public void run() throws RemoteException {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07003630 mAuthenticator.confirmCredentials(this, account, options);
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003631 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003632 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003633 protected String toDebugString(long now) {
3634 return super.toDebugString(now) + ", confirmCredentials"
3635 + ", " + account;
3636 }
3637 }.bind();
3638 } finally {
3639 restoreCallingIdentity(identityToken);
3640 }
Fred Quintana60307342009-03-24 22:48:12 -07003641 }
3642
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003643 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003644 public void updateCredentials(IAccountManagerResponse response, final Account account,
3645 final String authTokenType, final boolean expectActivityLaunch,
3646 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003647 Bundle.setDefusable(loginOptions, true);
Fred Quintana56285a62010-12-02 14:20:51 -08003648 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3649 Log.v(TAG, "updateCredentials: " + account
3650 + ", response " + response
3651 + ", authTokenType " + authTokenType
3652 + ", expectActivityLaunch " + expectActivityLaunch
3653 + ", caller's uid " + Binder.getCallingUid()
3654 + ", pid " + Binder.getCallingPid());
3655 }
Fred Quintana382601f2010-03-25 12:25:10 -07003656 if (response == null) throw new IllegalArgumentException("response is null");
3657 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003658 int userId = UserHandle.getCallingUserId();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003659 long identityToken = clearCallingIdentity();
3660 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003661 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003662 new Session(accounts, response, account.type, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003663 true /* stripAuthTokenFromResult */, account.name,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07003664 false /* authDetailsRequired */, true /* updateLastCredentialTime */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003665 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003666 public void run() throws RemoteException {
3667 mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
3668 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003669 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003670 protected String toDebugString(long now) {
3671 if (loginOptions != null) loginOptions.keySet();
3672 return super.toDebugString(now) + ", updateCredentials"
3673 + ", " + account
3674 + ", authTokenType " + authTokenType
3675 + ", loginOptions " + loginOptions;
3676 }
3677 }.bind();
3678 } finally {
3679 restoreCallingIdentity(identityToken);
3680 }
Fred Quintana60307342009-03-24 22:48:12 -07003681 }
3682
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08003683 @Override
Sandra Kwane68c37e2015-11-12 17:11:49 -08003684 public void startUpdateCredentialsSession(
3685 IAccountManagerResponse response,
3686 final Account account,
3687 final String authTokenType,
3688 final boolean expectActivityLaunch,
3689 final Bundle loginOptions) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003690 Bundle.setDefusable(loginOptions, true);
Sandra Kwane68c37e2015-11-12 17:11:49 -08003691 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3692 Log.v(TAG,
3693 "startUpdateCredentialsSession: " + account + ", response " + response
3694 + ", authTokenType " + authTokenType + ", expectActivityLaunch "
3695 + expectActivityLaunch + ", caller's uid " + Binder.getCallingUid()
3696 + ", pid " + Binder.getCallingPid());
3697 }
3698 if (response == null) {
3699 throw new IllegalArgumentException("response is null");
3700 }
3701 if (account == null) {
3702 throw new IllegalArgumentException("account is null");
3703 }
Sandra Kwana578d112015-12-16 16:01:43 -08003704
3705 final int uid = Binder.getCallingUid();
Sandra Kwane68c37e2015-11-12 17:11:49 -08003706 int userId = UserHandle.getCallingUserId();
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003707
3708 // Check to see if the Password should be included to the caller.
3709 String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
3710 boolean isPasswordForwardingAllowed = isPermitted(
Carlos Valdivia714bbd82016-04-22 14:10:40 -07003711 callerPkg, uid, Manifest.permission.GET_PASSWORD);
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003712
Sandra Kwane68c37e2015-11-12 17:11:49 -08003713 long identityToken = clearCallingIdentity();
3714 try {
3715 UserAccounts accounts = getUserAccounts(userId);
3716 new StartAccountSession(
3717 accounts,
3718 response,
3719 account.type,
3720 expectActivityLaunch,
3721 account.name,
3722 false /* authDetailsRequired */,
Carlos Valdivia51b651a2016-03-30 13:44:28 -07003723 true /* updateLastCredentialTime */,
3724 isPasswordForwardingAllowed) {
Sandra Kwane68c37e2015-11-12 17:11:49 -08003725 @Override
3726 public void run() throws RemoteException {
3727 mAuthenticator.startUpdateCredentialsSession(this, account, authTokenType,
3728 loginOptions);
3729 }
3730
3731 @Override
3732 protected String toDebugString(long now) {
3733 if (loginOptions != null)
3734 loginOptions.keySet();
3735 return super.toDebugString(now)
3736 + ", startUpdateCredentialsSession"
3737 + ", " + account
3738 + ", authTokenType " + authTokenType
3739 + ", loginOptions " + loginOptions;
3740 }
3741 }.bind();
3742 } finally {
3743 restoreCallingIdentity(identityToken);
3744 }
3745 }
3746
3747 @Override
Sandra Kwan390c9d22016-01-12 14:13:37 -08003748 public void isCredentialsUpdateSuggested(
3749 IAccountManagerResponse response,
3750 final Account account,
3751 final String statusToken) {
3752 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3753 Log.v(TAG,
3754 "isCredentialsUpdateSuggested: " + account + ", response " + response
3755 + ", caller's uid " + Binder.getCallingUid()
3756 + ", pid " + Binder.getCallingPid());
3757 }
3758 if (response == null) {
3759 throw new IllegalArgumentException("response is null");
3760 }
3761 if (account == null) {
3762 throw new IllegalArgumentException("account is null");
3763 }
3764 if (TextUtils.isEmpty(statusToken)) {
3765 throw new IllegalArgumentException("status token is empty");
3766 }
3767
Sandra Kwan390c9d22016-01-12 14:13:37 -08003768 int usrId = UserHandle.getCallingUserId();
3769 long identityToken = clearCallingIdentity();
3770 try {
3771 UserAccounts accounts = getUserAccounts(usrId);
3772 new Session(accounts, response, account.type, false /* expectActivityLaunch */,
3773 false /* stripAuthTokenFromResult */, account.name,
3774 false /* authDetailsRequired */) {
3775 @Override
3776 protected String toDebugString(long now) {
3777 return super.toDebugString(now) + ", isCredentialsUpdateSuggested"
3778 + ", " + account;
3779 }
3780
3781 @Override
3782 public void run() throws RemoteException {
3783 mAuthenticator.isCredentialsUpdateSuggested(this, account, statusToken);
3784 }
3785
3786 @Override
3787 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06003788 Bundle.setDefusable(result, true);
Sandra Kwan390c9d22016-01-12 14:13:37 -08003789 IAccountManagerResponse response = getResponseAndClose();
3790 if (response == null) {
3791 return;
3792 }
3793
3794 if (result == null) {
3795 sendErrorResponse(
3796 response,
3797 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3798 "null bundle");
3799 return;
3800 }
3801
3802 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3803 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
3804 + response);
3805 }
3806 // Check to see if an error occurred. We know if an error occurred because all
3807 // error codes are greater than 0.
3808 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0)) {
3809 sendErrorResponse(response,
3810 result.getInt(AccountManager.KEY_ERROR_CODE),
3811 result.getString(AccountManager.KEY_ERROR_MESSAGE));
3812 return;
3813 }
3814 if (!result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)) {
3815 sendErrorResponse(
3816 response,
3817 AccountManager.ERROR_CODE_INVALID_RESPONSE,
3818 "no result in response");
3819 return;
3820 }
3821 final Bundle newResult = new Bundle();
3822 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
3823 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
3824 sendResponse(response, newResult);
3825 }
3826 }.bind();
3827 } finally {
3828 restoreCallingIdentity(identityToken);
3829 }
3830 }
3831
3832 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07003833 public void editProperties(IAccountManagerResponse response, final String accountType,
3834 final boolean expectActivityLaunch) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003835 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08003836 if (Log.isLoggable(TAG, Log.VERBOSE)) {
3837 Log.v(TAG, "editProperties: accountType " + accountType
3838 + ", response " + response
3839 + ", expectActivityLaunch " + expectActivityLaunch
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003840 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08003841 + ", pid " + Binder.getCallingPid());
3842 }
Fred Quintana382601f2010-03-25 12:25:10 -07003843 if (response == null) throw new IllegalArgumentException("response is null");
3844 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00003845 int userId = UserHandle.getCallingUserId();
3846 if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07003847 String msg = String.format(
3848 "uid %s cannot edit authenticator properites for account type: %s",
3849 callingUid,
3850 accountType);
3851 throw new SecurityException(msg);
3852 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003853 long identityToken = clearCallingIdentity();
3854 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07003855 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08003856 new Session(accounts, response, accountType, expectActivityLaunch,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08003857 true /* stripAuthTokenFromResult */, null /* accountName */,
3858 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003859 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003860 public void run() throws RemoteException {
3861 mAuthenticator.editProperties(this, mAccountType);
3862 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07003863 @Override
Fred Quintana26fc5eb2009-04-09 15:05:50 -07003864 protected String toDebugString(long now) {
3865 return super.toDebugString(now) + ", editProperties"
3866 + ", accountType " + accountType;
3867 }
3868 }.bind();
3869 } finally {
3870 restoreCallingIdentity(identityToken);
3871 }
Fred Quintana60307342009-03-24 22:48:12 -07003872 }
3873
Amith Yamasani12747872015-12-07 14:19:49 -08003874 @Override
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003875 public boolean hasAccountAccess(@NonNull Account account, @NonNull String packageName,
3876 @NonNull UserHandle userHandle) {
3877 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
3878 throw new SecurityException("Can be called only by system UID");
3879 }
3880 Preconditions.checkNotNull(account, "account cannot be null");
3881 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3882 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3883
3884 final int userId = userHandle.getIdentifier();
3885
3886 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3887
3888 try {
3889
3890 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3891 // Use null token which means any token. Having a token means the package
3892 // is trusted by the authenticator, hence it is fine to access the account.
3893 if (permissionIsGranted(account, null, uid, userId)) {
3894 return true;
3895 }
3896 // In addition to the permissions required to get an auth token we also allow
3897 // the account to be accessed by holders of the get accounts permissions.
3898 return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
3899 || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
3900 } catch (NameNotFoundException e) {
3901 return false;
3902 }
3903 }
3904
3905 private boolean checkUidPermission(String permission, int uid, String opPackageName) {
3906 final long identity = Binder.clearCallingIdentity();
3907 try {
3908 IPackageManager pm = ActivityThread.getPackageManager();
3909 if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
3910 return false;
3911 }
3912 final int opCode = AppOpsManager.permissionToOpCode(permission);
3913 return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
3914 opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
3915 } catch (RemoteException e) {
3916 /* ignore - local call */
3917 } finally {
3918 Binder.restoreCallingIdentity(identity);
3919 }
3920 return false;
3921 }
3922
3923 @Override
3924 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
3925 @NonNull String packageName, @NonNull UserHandle userHandle) {
3926 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
3927 throw new SecurityException("Can be called only by system UID");
3928 }
3929
3930 Preconditions.checkNotNull(account, "account cannot be null");
3931 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3932 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3933
3934 final int userId = userHandle.getIdentifier();
3935
3936 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3937
3938 final int uid;
3939 try {
3940 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3941 } catch (NameNotFoundException e) {
3942 Slog.e(TAG, "Unknown package " + packageName);
3943 return null;
3944 }
3945
3946 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
3947
3948 return PendingIntent.getActivityAsUser(
3949 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
3950 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3951 null, new UserHandle(userId)).getIntentSender();
3952 }
3953
3954 private Intent newRequestAccountAccessIntent(Account account, String packageName,
3955 int uid, RemoteCallback callback) {
3956 return newGrantCredentialsPermissionIntent(account, packageName, uid,
3957 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
3958 @Override
3959 public void onResult(Bundle value) throws RemoteException {
3960 handleAuthenticatorResponse(true);
3961 }
3962
3963 @Override
3964 public void onRequestContinued() {
3965 /* ignore */
3966 }
3967
3968 @Override
3969 public void onError(int errorCode, String errorMessage) throws RemoteException {
3970 handleAuthenticatorResponse(false);
3971 }
3972
3973 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
3974 cancelNotification(getCredentialPermissionNotificationId(account,
3975 AccountManager.ACCOUNT_ACCESS_TOKEN, uid), packageName,
3976 UserHandle.getUserHandleForUid(uid));
3977 if (callback != null) {
3978 Bundle result = new Bundle();
3979 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
3980 callback.sendResult(result);
3981 }
3982 }
3983 }), AccountManager.ACCOUNT_ACCESS_TOKEN, false);
3984 }
3985
3986 @Override
Amith Yamasani12747872015-12-07 14:19:49 -08003987 public boolean someUserHasAccount(@NonNull final Account account) {
3988 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
3989 throw new SecurityException("Only system can check for accounts across users");
3990 }
3991 final long token = Binder.clearCallingIdentity();
3992 try {
3993 AccountAndUser[] allAccounts = getAllAccounts();
3994 for (int i = allAccounts.length - 1; i >= 0; i--) {
3995 if (allAccounts[i].account.equals(account)) {
3996 return true;
3997 }
3998 }
3999 return false;
4000 } finally {
4001 Binder.restoreCallingIdentity(token);
4002 }
4003 }
4004
Fred Quintana33269202009-04-20 16:05:10 -07004005 private class GetAccountsByTypeAndFeatureSession extends Session {
4006 private final String[] mFeatures;
4007 private volatile Account[] mAccountsOfType = null;
4008 private volatile ArrayList<Account> mAccountsWithFeatures = null;
4009 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004010 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07004011
Amith Yamasani04e0d262012-02-14 11:50:53 -08004012 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004013 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004014 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004015 true /* stripAuthTokenFromResult */, null /* accountName */,
4016 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004017 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07004018 mFeatures = features;
4019 }
4020
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004021 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004022 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004023 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004024 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
4025 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004026 }
Fred Quintana33269202009-04-20 16:05:10 -07004027 // check whether each account matches the requested features
Tejas Khorana5edff3b2016-06-28 20:59:52 -07004028 mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
Fred Quintana33269202009-04-20 16:05:10 -07004029 mCurrentAccount = 0;
4030
4031 checkAccount();
4032 }
4033
4034 public void checkAccount() {
4035 if (mCurrentAccount >= mAccountsOfType.length) {
4036 sendResult();
4037 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07004038 }
Fred Quintana33269202009-04-20 16:05:10 -07004039
Fred Quintana29e94b82010-03-10 12:11:51 -08004040 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
4041 if (accountAuthenticator == null) {
4042 // It is possible that the authenticator has died, which is indicated by
4043 // mAuthenticator being set to null. If this happens then just abort.
4044 // There is no need to send back a result or error in this case since
4045 // that already happened when mAuthenticator was cleared.
4046 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4047 Log.v(TAG, "checkAccount: aborting session since we are no longer"
4048 + " connected to the authenticator, " + toDebugString());
4049 }
4050 return;
4051 }
Fred Quintana33269202009-04-20 16:05:10 -07004052 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08004053 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07004054 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004055 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07004056 }
4057 }
4058
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004059 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004060 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004061 Bundle.setDefusable(result, true);
Fred Quintana33269202009-04-20 16:05:10 -07004062 mNumResults++;
4063 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004064 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07004065 return;
4066 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004067 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07004068 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
4069 }
4070 mCurrentAccount++;
4071 checkAccount();
4072 }
4073
4074 public void sendResult() {
4075 IAccountManagerResponse response = getResponseAndClose();
4076 if (response != null) {
4077 try {
4078 Account[] accounts = new Account[mAccountsWithFeatures.size()];
4079 for (int i = 0; i < accounts.length; i++) {
4080 accounts[i] = mAccountsWithFeatures.get(i);
4081 }
Fred Quintana56285a62010-12-02 14:20:51 -08004082 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4083 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
4084 + response);
4085 }
Fred Quintana33269202009-04-20 16:05:10 -07004086 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004087 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07004088 response.onResult(result);
4089 } catch (RemoteException e) {
4090 // if the caller is dead then there is no one to care about remote exceptions
4091 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4092 Log.v(TAG, "failure while notifying response", e);
4093 }
4094 }
4095 }
4096 }
4097
4098
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004099 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004100 protected String toDebugString(long now) {
4101 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
4102 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
4103 }
4104 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004105
Amith Yamasani04e0d262012-02-14 11:50:53 -08004106 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004107 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08004108 * @hide
4109 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004110 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004111 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004112 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07004113 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4114 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004115 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004116 return new Account[0];
4117 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004118 long identityToken = clearCallingIdentity();
4119 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004120 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004121 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004122 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004123 callingUid,
4124 null, // packageName
4125 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004126 } finally {
4127 restoreCallingIdentity(identityToken);
4128 }
4129 }
4130
Amith Yamasanif29f2362012-04-05 18:29:52 -07004131 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004132 * Returns accounts for all running users.
4133 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07004134 * @hide
4135 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004136 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004137 public AccountAndUser[] getRunningAccounts() {
4138 final int[] runningUserIds;
4139 try {
4140 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
4141 } catch (RemoteException e) {
4142 // Running in system_server; should never happen
4143 throw new RuntimeException(e);
4144 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004145 return getAccounts(runningUserIds);
4146 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07004147
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004148 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004149 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004150 public AccountAndUser[] getAllAccounts() {
Amith Yamasanid04aaa32016-06-13 12:09:36 -07004151 final List<UserInfo> users = getUserManager().getUsers(true);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004152 final int[] userIds = new int[users.size()];
4153 for (int i = 0; i < userIds.length; i++) {
4154 userIds[i] = users.get(i).id;
4155 }
4156 return getAccounts(userIds);
4157 }
4158
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004159 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004160 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004161 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004162 for (int userId : userIds) {
4163 UserAccounts userAccounts = getUserAccounts(userId);
4164 if (userAccounts == null) continue;
4165 synchronized (userAccounts.cacheLock) {
4166 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
4167 Binder.getCallingUid(), null);
4168 for (int a = 0; a < accounts.length; a++) {
4169 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07004170 }
4171 }
4172 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004173
4174 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
4175 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07004176 }
4177
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004178 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004179 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004180 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
4181 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004182 }
4183
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004184 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004185 private Account[] getAccountsAsUser(
4186 String type,
4187 int userId,
4188 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004189 int packageUid,
4190 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004191 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004192 // Only allow the system process to read accounts of other users
4193 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07004194 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08004195 && mContext.checkCallingOrSelfPermission(
4196 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
4197 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004198 throw new SecurityException("User " + UserHandle.getCallingUserId()
4199 + " trying to get account for " + userId);
4200 }
4201
Fred Quintana56285a62010-12-02 14:20:51 -08004202 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4203 Log.v(TAG, "getAccounts: accountType " + type
4204 + ", caller's uid " + Binder.getCallingUid()
4205 + ", pid " + Binder.getCallingPid());
4206 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004207 // If the original calling app was using the framework account chooser activity, we'll
4208 // be passed in the original caller's uid here, which is what should be used for filtering.
4209 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
4210 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07004211 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07004212 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004213 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4214 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004215 if (visibleAccountTypes.isEmpty()
4216 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004217 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004218 } else if (visibleAccountTypes.contains(type)) {
4219 // Prune the list down to just the requested type.
4220 visibleAccountTypes = new ArrayList<>();
4221 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004222 } // else aggregate all the visible accounts (it won't matter if the
4223 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004224
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004225 long identityToken = clearCallingIdentity();
4226 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004227 UserAccounts accounts = getUserAccounts(userId);
Tejas Khorana69990d92016-08-03 11:19:40 -07004228 Account[] accountsToReturn = getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004229 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004230 callingUid,
4231 callingPackage,
4232 visibleAccountTypes);
Tejas Khorana69990d92016-08-03 11:19:40 -07004233 ArrayList<Account> accountsToReturnList = new
4234 ArrayList<Account>(Arrays.asList(accountsToReturn));
4235 for(int i = accountsToReturnList.size() - 1; i >= 0 ; i--) {
4236 // if account not visible to caller or managed by caller, remove from
4237 // accounts to return. Note that all accounts visible by default unless
4238 // visible list functionality implemented
4239 if(!(isAccountVisible(accountsToReturnList.get(i), callingUid,
4240 getUserAccounts(userId)))) {
4241 accountsToReturnList.remove(i);
4242 }
4243 }
4244 return accountsToReturnList.toArray(new Account[accountsToReturnList.size()]);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004245 } finally {
4246 restoreCallingIdentity(identityToken);
4247 }
4248 }
4249
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004250 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004251 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004252 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004253 int callingUid,
4254 String callingPackage,
4255 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004256 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004257 ArrayList<Account> visibleAccounts = new ArrayList<>();
4258 for (String visibleType : visibleAccountTypes) {
4259 Account[] accountsForType = getAccountsFromCacheLocked(
4260 userAccounts, visibleType, callingUid, callingPackage);
4261 if (accountsForType != null) {
4262 visibleAccounts.addAll(Arrays.asList(accountsForType));
4263 }
4264 }
4265 Account[] result = new Account[visibleAccounts.size()];
4266 for (int i = 0; i < visibleAccounts.size(); i++) {
4267 result[i] = visibleAccounts.get(i);
4268 }
4269 return result;
4270 }
4271 }
4272
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004273 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004274 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07004275 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004276 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
4277 for (Account account : accounts) {
4278 addSharedAccountAsUser(account, userId);
4279 }
4280 }
4281
4282 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004283 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004284 UserAccounts accounts = getUserAccounts(userId);
4285 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004286 AccountsDbUtils.deleteSharedAccount(db, account);
4287 long accountId = AccountsDbUtils.insertSharedAccount(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004288 if (accountId < 0) {
4289 Log.w(TAG, "insertAccountIntoDatabase: " + account
4290 + ", skipping the DB insert failed");
4291 return false;
4292 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004293 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004294 return true;
4295 }
4296
4297 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004298 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
4299 userId = handleIncomingUser(userId);
4300 UserAccounts accounts = getUserAccounts(userId);
4301 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004302 long sharedTableAccountId = AccountsDbUtils.findSharedAccountId(db, account);
4303 int r = AccountsDbUtils.renameSharedAccount(db, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004304 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004305 int callingUid = getCallingUid();
4306 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
4307 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004308 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004309 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004310 }
4311 return r > 0;
4312 }
4313
4314 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08004315 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004316 return removeSharedAccountAsUser(account, userId, getCallingUid());
4317 }
4318
4319 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004320 userId = handleIncomingUser(userId);
4321 UserAccounts accounts = getUserAccounts(userId);
4322 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004323 long sharedTableAccountId = AccountsDbUtils.findSharedAccountId(db, account);
4324 boolean deleted = AccountsDbUtils.deleteSharedAccount(db, account);
4325 if (deleted) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004326 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
4327 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07004328 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004329 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004330 return deleted;
Amith Yamasani67df64b2012-12-14 12:09:36 -08004331 }
4332
4333 @Override
4334 public Account[] getSharedAccountsAsUser(int userId) {
4335 userId = handleIncomingUser(userId);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004336 SQLiteDatabase db = getUserAccounts(userId).openHelper.getReadableDatabase();
4337 List<Account> accountList = AccountsDbUtils.getSharedAccounts(db);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004338 Account[] accountArray = new Account[accountList.size()];
4339 accountList.toArray(accountArray);
4340 return accountArray;
4341 }
4342
4343 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004344 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004345 public Account[] getAccounts(String type, String opPackageName) {
Tejas Khorana69990d92016-08-03 11:19:40 -07004346 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004347 }
4348
Amith Yamasani27db4682013-03-30 17:07:47 -07004349 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004350 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004351 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004352 int callingUid = Binder.getCallingUid();
4353 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
4354 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
4355 + callingUid + " with uid=" + uid);
4356 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004357 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
4358 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004359 }
4360
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004361 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004362 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004363 public Account[] getAccountsByTypeForPackage(String type, String packageName,
4364 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004365 int packageUid = -1;
4366 try {
4367 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07004368 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
4369 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004370 } catch (RemoteException re) {
4371 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
4372 return new Account[0];
4373 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004374 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
4375 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004376 }
4377
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004378 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004379 public void getAccountsByFeatures(
4380 IAccountManagerResponse response,
4381 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004382 String[] features,
4383 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004384 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08004385 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4386 Log.v(TAG, "getAccounts: accountType " + type
4387 + ", response " + response
4388 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004389 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08004390 + ", pid " + Binder.getCallingPid());
4391 }
Fred Quintana382601f2010-03-25 12:25:10 -07004392 if (response == null) throw new IllegalArgumentException("response is null");
4393 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004394 int userId = UserHandle.getCallingUserId();
4395
Svetoslavf3f02ac2015-09-08 14:36:35 -07004396 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4397 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004398 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004399 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004400 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004401 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
4402 try {
4403 response.onResult(result);
4404 } catch (RemoteException e) {
4405 Log.e(TAG, "Cannot respond to caller do to exception." , e);
4406 }
4407 return;
4408 }
Fred Quintana33269202009-04-20 16:05:10 -07004409 long identityToken = clearCallingIdentity();
4410 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004411 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004412 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004413 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004414 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004415 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004416 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08004417 Bundle result = new Bundle();
4418 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4419 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004420 return;
4421 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004422 new GetAccountsByTypeAndFeatureSession(
4423 userAccounts,
4424 response,
4425 type,
4426 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004427 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07004428 } finally {
4429 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07004430 }
4431 }
4432
Fred Quintanaa698f422009-04-08 19:14:54 -07004433 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07004434 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07004435 IAccountManagerResponse mResponse;
4436 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004437 final boolean mExpectActivityLaunch;
4438 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004439 final String mAccountName;
4440 // Indicates if we need to add auth details(like last credential time)
4441 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004442 // If set, we need to update the last authenticated time. This is
4443 // currently
4444 // used on
4445 // successful confirming credentials.
4446 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07004447
Fred Quintana33269202009-04-20 16:05:10 -07004448 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07004449 private int mNumRequestContinued = 0;
4450 private int mNumErrors = 0;
4451
Fred Quintana60307342009-03-24 22:48:12 -07004452 IAccountAuthenticator mAuthenticator = null;
4453
Fred Quintana8570f742010-02-18 10:32:54 -08004454 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004455 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004456
Amith Yamasani04e0d262012-02-14 11:50:53 -08004457 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004458 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4459 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004460 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4461 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4462 }
4463
4464 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4465 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4466 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07004467 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08004468 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07004469 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08004470 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004471 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07004472 mResponse = response;
4473 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004474 mExpectActivityLaunch = expectActivityLaunch;
4475 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004476 mAccountName = accountName;
4477 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004478 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004479
Fred Quintanaa698f422009-04-08 19:14:54 -07004480 synchronized (mSessions) {
4481 mSessions.put(toString(), this);
4482 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004483 if (response != null) {
4484 try {
4485 response.asBinder().linkToDeath(this, 0 /* flags */);
4486 } catch (RemoteException e) {
4487 mResponse = null;
4488 binderDied();
4489 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004490 }
Fred Quintana60307342009-03-24 22:48:12 -07004491 }
4492
Fred Quintanaa698f422009-04-08 19:14:54 -07004493 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07004494 if (mResponse == null) {
4495 // this session has already been closed
4496 return null;
4497 }
Fred Quintana60307342009-03-24 22:48:12 -07004498 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07004499 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07004500 return response;
4501 }
4502
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004503 /**
4504 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4505 * security policy.
4506 *
4507 * In particular we want to make sure that the Authenticator doesn't try to trick users
4508 * into launching aribtrary intents on the device via by tricking to click authenticator
4509 * supplied entries in the system Settings app.
4510 */
4511 protected void checkKeyIntent(
4512 int authUid,
4513 Intent intent) throws SecurityException {
4514 long bid = Binder.clearCallingIdentity();
4515 try {
4516 PackageManager pm = mContext.getPackageManager();
4517 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4518 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4519 int targetUid = targetActivityInfo.applicationInfo.uid;
Sandra Kwan0e961a12016-06-30 14:34:01 -07004520 if (!GrantCredentialsPermissionActivity.class.getName().equals(
4521 targetActivityInfo.getClass().getName())
4522 && !CantAddAccountActivity.class
4523 .equals(targetActivityInfo.getClass().getName())
4524 && PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
4525 targetUid)) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004526 String pkgName = targetActivityInfo.packageName;
4527 String activityName = targetActivityInfo.name;
4528 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4529 + "does not share a signature with the supplying authenticator (%s).";
4530 throw new SecurityException(
4531 String.format(tmpl, activityName, pkgName, mAccountType));
4532 }
4533 } finally {
4534 Binder.restoreCallingIdentity(bid);
4535 }
4536 }
4537
Fred Quintanaa698f422009-04-08 19:14:54 -07004538 private void close() {
4539 synchronized (mSessions) {
4540 if (mSessions.remove(toString()) == null) {
4541 // the session was already closed, so bail out now
4542 return;
4543 }
4544 }
4545 if (mResponse != null) {
4546 // stop listening for response deaths
4547 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4548
4549 // clear this so that we don't accidentally send any further results
4550 mResponse = null;
4551 }
4552 cancelTimeout();
4553 unbind();
4554 }
4555
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004556 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004557 public void binderDied() {
4558 mResponse = null;
4559 close();
4560 }
4561
4562 protected String toDebugString() {
4563 return toDebugString(SystemClock.elapsedRealtime());
4564 }
4565
4566 protected String toDebugString(long now) {
4567 return "Session: expectLaunch " + mExpectActivityLaunch
4568 + ", connected " + (mAuthenticator != null)
4569 + ", stats (" + mNumResults + "/" + mNumRequestContinued
4570 + "/" + mNumErrors + ")"
4571 + ", lifetime " + ((now - mCreationTime) / 1000.0);
4572 }
4573
Fred Quintana60307342009-03-24 22:48:12 -07004574 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004575 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4576 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4577 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004578 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004579 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004580 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07004581 }
4582 }
4583
4584 private void unbind() {
4585 if (mAuthenticator != null) {
4586 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07004587 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07004588 }
4589 }
4590
Fred Quintana60307342009-03-24 22:48:12 -07004591 public void cancelTimeout() {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004592 mHandler.removeMessages(MESSAGE_TIMED_OUT, this);
Fred Quintana60307342009-03-24 22:48:12 -07004593 }
4594
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004595 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004596 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07004597 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07004598 try {
4599 run();
4600 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004601 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07004602 "remote exception");
4603 }
Fred Quintana60307342009-03-24 22:48:12 -07004604 }
4605
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004606 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004607 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004608 mAuthenticator = null;
4609 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004610 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004611 try {
4612 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4613 "disconnected");
4614 } catch (RemoteException e) {
4615 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4616 Log.v(TAG, "Session.onServiceDisconnected: "
4617 + "caught RemoteException while responding", e);
4618 }
4619 }
Fred Quintana60307342009-03-24 22:48:12 -07004620 }
4621 }
4622
Fred Quintanab839afc2009-10-14 15:57:28 -07004623 public abstract void run() throws RemoteException;
4624
Fred Quintana60307342009-03-24 22:48:12 -07004625 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004626 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004627 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004628 try {
4629 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4630 "timeout");
4631 } catch (RemoteException e) {
4632 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4633 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4634 e);
4635 }
4636 }
Fred Quintana60307342009-03-24 22:48:12 -07004637 }
4638 }
4639
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004640 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004641 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004642 Bundle.setDefusable(result, true);
Fred Quintanaa698f422009-04-08 19:14:54 -07004643 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004644 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004645 if (result != null) {
4646 boolean isSuccessfulConfirmCreds = result.getBoolean(
4647 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004648 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004649 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4650 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004651 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004652 // mUpdateLastAuthenticatedTime is true and the confirmRequest
4653 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07004654 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004655 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004656 if (needUpdate || mAuthDetailsRequired) {
4657 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4658 if (needUpdate && accountPresent) {
4659 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4660 }
4661 if (mAuthDetailsRequired) {
4662 long lastAuthenticatedTime = -1;
4663 if (accountPresent) {
4664 lastAuthenticatedTime = DatabaseUtils.longForQuery(
4665 mAccounts.openHelper.getReadableDatabase(),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004666 "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4667 + " FROM " +
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004668 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
4669 + ACCOUNTS_TYPE + "=?",
4670 new String[] {
4671 mAccountName, mAccountType
4672 });
4673 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07004674 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004675 lastAuthenticatedTime);
4676 }
4677 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004678 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004679 if (result != null
4680 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004681 checkKeyIntent(
4682 Binder.getCallingUid(),
4683 intent);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004684 }
4685 if (result != null
4686 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004687 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4688 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004689 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4690 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004691 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4692 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004693 }
Fred Quintana60307342009-03-24 22:48:12 -07004694 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004695 IAccountManagerResponse response;
4696 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004697 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004698 response = mResponse;
4699 } else {
4700 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004701 }
Fred Quintana60307342009-03-24 22:48:12 -07004702 if (response != null) {
4703 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07004704 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08004705 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4706 Log.v(TAG, getClass().getSimpleName()
4707 + " calling onError() on response " + response);
4708 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004709 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07004710 "null bundle returned");
4711 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08004712 if (mStripAuthTokenFromResult) {
4713 result.remove(AccountManager.KEY_AUTHTOKEN);
4714 }
Fred Quintana56285a62010-12-02 14:20:51 -08004715 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4716 Log.v(TAG, getClass().getSimpleName()
4717 + " calling onResult() on response " + response);
4718 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004719 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4720 (intent == null)) {
4721 // All AccountManager error codes are greater than 0
4722 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4723 result.getString(AccountManager.KEY_ERROR_MESSAGE));
4724 } else {
4725 response.onResult(result);
4726 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004727 }
Fred Quintana60307342009-03-24 22:48:12 -07004728 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004729 // if the caller is dead then there is no one to care about remote exceptions
4730 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4731 Log.v(TAG, "failure while notifying response", e);
4732 }
Fred Quintana60307342009-03-24 22:48:12 -07004733 }
4734 }
4735 }
Fred Quintana60307342009-03-24 22:48:12 -07004736
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004737 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004738 public void onRequestContinued() {
4739 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07004740 }
4741
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004742 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004743 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004744 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07004745 IAccountManagerResponse response = getResponseAndClose();
4746 if (response != null) {
4747 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08004748 Log.v(TAG, getClass().getSimpleName()
4749 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07004750 }
4751 try {
4752 response.onError(errorCode, errorMessage);
4753 } catch (RemoteException e) {
4754 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4755 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4756 }
4757 }
4758 } else {
4759 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4760 Log.v(TAG, "Session.onError: already closed");
4761 }
Fred Quintana60307342009-03-24 22:48:12 -07004762 }
4763 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004764
4765 /**
4766 * find the component name for the authenticator and initiate a bind
4767 * if no authenticator or the bind fails then return false, otherwise return true
4768 */
4769 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004770 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4771 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4772 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07004773 if (authenticatorInfo == null) {
4774 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4775 Log.v(TAG, "there is no authenticator for " + authenticatorType
4776 + ", bailing out");
4777 }
4778 return false;
4779 }
4780
Jeff Sharkeyce18c812016-04-27 16:00:41 -06004781 if (!isLocalUnlockedUser(mAccounts.userId)
Jeff Sharkey8a372a02016-03-16 16:25:45 -06004782 && !authenticatorInfo.componentInfo.directBootAware) {
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07004783 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4784 + " which isn't encryption aware");
4785 return false;
4786 }
4787
Fred Quintanab839afc2009-10-14 15:57:28 -07004788 Intent intent = new Intent();
4789 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4790 intent.setComponent(authenticatorInfo.componentName);
4791 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4792 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4793 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08004794 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004795 UserHandle.of(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07004796 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4797 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4798 }
4799 return false;
4800 }
4801
Fred Quintanab839afc2009-10-14 15:57:28 -07004802 return true;
4803 }
Fred Quintana60307342009-03-24 22:48:12 -07004804 }
4805
4806 private class MessageHandler extends Handler {
4807 MessageHandler(Looper looper) {
4808 super(looper);
4809 }
Costin Manolache3348f142009-09-29 18:58:36 -07004810
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004811 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004812 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07004813 switch (msg.what) {
4814 case MESSAGE_TIMED_OUT:
4815 Session session = (Session)msg.obj;
4816 session.onTimedOut();
4817 break;
4818
Amith Yamasani5be347b2013-03-31 17:44:31 -07004819 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00004820 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07004821 break;
4822
Fred Quintana60307342009-03-24 22:48:12 -07004823 default:
4824 throw new IllegalStateException("unhandled message: " + msg.what);
4825 }
4826 }
4827 }
4828
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004829 @VisibleForTesting
4830 String getPreNDatabaseName(int userId) {
Jeff Sharkey8212ae02016-02-10 14:46:43 -07004831 File systemDir = Environment.getDataSystemDirectory();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004832 File databaseFile = new File(Environment.getUserSystemDirectory(userId),
4833 PRE_N_DATABASE_NAME);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004834 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07004835 // Migrate old file, if it exists, to the new location.
4836 // Make sure the new file doesn't already exist. A dummy file could have been
4837 // accidentally created in the old location, causing the new one to become corrupted
4838 // as well.
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004839 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07004840 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07004841 // Check for use directory; create if it doesn't exist, else renameTo will fail
Amith Yamasani61f57372012-08-31 12:12:28 -07004842 File userDir = Environment.getUserSystemDirectory(userId);
Marc Blankc6b0f992012-03-18 19:16:41 -07004843 if (!userDir.exists()) {
4844 if (!userDir.mkdirs()) {
4845 throw new IllegalStateException("User dir cannot be created: " + userDir);
4846 }
4847 }
4848 if (!oldFile.renameTo(databaseFile)) {
4849 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
4850 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004851 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08004852 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004853 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08004854 }
4855
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004856 @VisibleForTesting
4857 String getDeDatabaseName(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004858 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
4859 DE_DATABASE_NAME);
4860 return databaseFile.getPath();
4861 }
4862
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004863 @VisibleForTesting
4864 String getCeDatabaseName(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004865 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
4866 CE_DATABASE_NAME);
4867 return databaseFile.getPath();
4868 }
4869
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004870 private static class DebugDbHelper{
4871 private DebugDbHelper() {
4872 }
4873
4874 private static String TABLE_DEBUG = "debug_table";
4875
4876 // Columns for the table
4877 private static String ACTION_TYPE = "action_type";
4878 private static String TIMESTAMP = "time";
4879 private static String CALLER_UID = "caller_uid";
4880 private static String TABLE_NAME = "table_name";
4881 private static String KEY = "primary_key";
4882
4883 // These actions correspond to the occurrence of real actions. Since
4884 // these are called by the authenticators, the uid associated will be
4885 // of the authenticator.
4886 private static String ACTION_SET_PASSWORD = "action_set_password";
4887 private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
4888 private static String ACTION_ACCOUNT_ADD = "action_account_add";
4889 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
Fyodor Kupolov35f68082016-04-06 12:14:17 -07004890 private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004891 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
4892 private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
4893
4894 // These actions don't necessarily correspond to any action on
4895 // accountDb taking place. As an example, there might be a request for
4896 // addingAccount, which might not lead to addition of account on grounds
4897 // of bad authentication. We will still be logging it to keep track of
4898 // who called.
4899 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
4900 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
Fyodor Kupolov35f68082016-04-06 12:14:17 -07004901 private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004902
Sandra Kwan920f6ef2015-11-10 14:13:29 -08004903 //This action doesn't add account to accountdb. Account is only
4904 // added in finishSession which may be in a different user profile.
Sandra Kwan78812282015-11-04 11:19:47 -08004905 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
Sandra Kwan920f6ef2015-11-10 14:13:29 -08004906 private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
4907 "action_called_account_session_finish";
Sandra Kwan78812282015-11-04 11:19:47 -08004908
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004909 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4910
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004911 private static void createDebugTable(SQLiteDatabase db) {
4912 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
4913 + ACCOUNTS_ID + " INTEGER,"
4914 + ACTION_TYPE + " TEXT NOT NULL, "
4915 + TIMESTAMP + " DATETIME,"
4916 + CALLER_UID + " INTEGER NOT NULL,"
4917 + TABLE_NAME + " TEXT NOT NULL,"
4918 + KEY + " INTEGER PRIMARY KEY)");
4919 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
4920 }
4921 }
4922
4923 private void logRecord(UserAccounts accounts, String action, String tableName) {
4924 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4925 logRecord(db, action, tableName, -1, accounts);
4926 }
4927
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004928 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
4929 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4930 logRecord(db, action, tableName, -1, accounts, uid);
4931 }
4932
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004933 /*
4934 * This function receives an opened writable database.
4935 */
4936 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4937 UserAccounts userAccount) {
4938 logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
4939 }
4940
4941 /*
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004942 * This function receives an opened writable database and writes to it in a separate thread.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004943 */
4944 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4945 UserAccounts userAccount, int callingUid) {
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004946
4947 class LogRecordTask implements Runnable {
4948 private final String action;
4949 private final String tableName;
4950 private final long accountId;
4951 private final UserAccounts userAccount;
4952 private final int callingUid;
4953 private final long userDebugDbInsertionPoint;
4954
4955 LogRecordTask(final String action,
4956 final String tableName,
4957 final long accountId,
4958 final UserAccounts userAccount,
4959 final int callingUid,
4960 final long userDebugDbInsertionPoint) {
4961 this.action = action;
4962 this.tableName = tableName;
4963 this.accountId = accountId;
4964 this.userAccount = userAccount;
4965 this.callingUid = callingUid;
4966 this.userDebugDbInsertionPoint = userDebugDbInsertionPoint;
4967 }
4968
4969 public void run() {
4970 SQLiteStatement logStatement = userAccount.statementForLogging;
4971 logStatement.bindLong(1, accountId);
4972 logStatement.bindString(2, action);
4973 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
4974 logStatement.bindLong(4, callingUid);
4975 logStatement.bindString(5, tableName);
4976 logStatement.bindLong(6, userDebugDbInsertionPoint);
4977 logStatement.execute();
4978 logStatement.clearBindings();
4979 }
4980 }
4981
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004982 LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
4983 callingUid, userAccount.debugDbInsertionPoint);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004984 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
4985 % MAX_DEBUG_DB_SIZE;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004986 mHandler.post(logTask);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004987 }
4988
4989 /*
4990 * This should only be called once to compile the sql statement for logging
4991 * and to find the insertion point.
4992 */
4993 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
4994 UserAccounts userAccount) {
4995 // Initialize the count if not done earlier.
4996 int size = (int) getDebugTableRowCount(db);
4997 if (size >= MAX_DEBUG_DB_SIZE) {
4998 // Table is full, and we need to find the point where to insert.
4999 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
5000 } else {
5001 userAccount.debugDbInsertionPoint = size;
5002 }
5003 compileSqlStatementForLogging(db, userAccount);
5004 }
5005
5006 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
5007 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
5008 + " VALUES (?,?,?,?,?,?)";
5009 userAccount.statementForLogging = db.compileStatement(sql);
5010 }
5011
5012 private long getDebugTableRowCount(SQLiteDatabase db) {
5013 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
5014 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
5015 }
5016
5017 /*
5018 * Finds the row key where the next insertion should take place. This should
5019 * be invoked only if the table has reached its full capacity.
5020 */
5021 private long getDebugTableInsertionPoint(SQLiteDatabase db) {
5022 // This query finds the smallest timestamp value (and if 2 records have
5023 // same timestamp, the choose the lower id).
5024 String queryCountDebugDbRows = new StringBuilder()
5025 .append("SELECT ").append(DebugDbHelper.KEY)
5026 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
5027 .append(" ORDER BY ")
5028 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
5029 .append(" LIMIT 1")
5030 .toString();
5031 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
5032 }
5033
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005034 static class PreNDatabaseHelper extends SQLiteOpenHelper {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005035 private final Context mContext;
5036 private final int mUserId;
5037
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005038 public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
5039 super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005040 mContext = context;
5041 mUserId = userId;
Fred Quintana60307342009-03-24 22:48:12 -07005042 }
5043
5044 @Override
5045 public void onCreate(SQLiteDatabase db) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005046 // We use PreNDatabaseHelper only if pre-N db exists
5047 throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005048 }
5049
Amith Yamasani67df64b2012-12-14 12:09:36 -08005050 private void createSharedAccountsTable(SQLiteDatabase db) {
5051 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
5052 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5053 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5054 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5055 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5056 }
5057
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08005058 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
5059 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
5060 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
5061 }
5062
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005063 private void addOldAccountNameColumn(SQLiteDatabase db) {
5064 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
5065 }
5066
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005067 private void addDebugTable(SQLiteDatabase db) {
5068 DebugDbHelper.createDebugTable(db);
5069 }
5070
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005071 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07005072 db.execSQL(""
5073 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5074 + " BEGIN"
5075 + " DELETE FROM " + TABLE_AUTHTOKENS
5076 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5077 + " DELETE FROM " + TABLE_EXTRAS
5078 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005079 + " DELETE FROM " + TABLE_GRANTS
5080 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07005081 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07005082 }
5083
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005084 private void createGrantsTable(SQLiteDatabase db) {
5085 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
5086 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5087 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
5088 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
5089 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
5090 + "," + GRANTS_GRANTEE_UID + "))");
5091 }
5092
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005093 private void populateMetaTableWithAuthTypeAndUID(SQLiteDatabase db,
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005094 Map<String, Integer> authTypeAndUIDMap) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005095 for (Entry<String, Integer> entry : authTypeAndUIDMap.entrySet()) {
5096 AccountsDbUtils.insertMetaAuthTypeAndUid(db, entry.getKey(), entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005097 }
5098 }
5099
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005100 /**
5101 * Pre-N database may need an upgrade before splitting
5102 */
Fred Quintana60307342009-03-24 22:48:12 -07005103 @Override
5104 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07005105 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07005106
Fred Quintanaa698f422009-04-08 19:14:54 -07005107 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005108 // no longer need to do anything since the work is done
5109 // when upgrading from version 2
5110 oldVersion++;
5111 }
5112
5113 if (oldVersion == 2) {
5114 createGrantsTable(db);
5115 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
5116 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07005117 oldVersion++;
5118 }
Costin Manolache3348f142009-09-29 18:58:36 -07005119
5120 if (oldVersion == 3) {
5121 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
5122 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
5123 oldVersion++;
5124 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08005125
5126 if (oldVersion == 4) {
5127 createSharedAccountsTable(db);
5128 oldVersion++;
5129 }
5130
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005131 if (oldVersion == 5) {
5132 addOldAccountNameColumn(db);
5133 oldVersion++;
5134 }
5135
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08005136 if (oldVersion == 6) {
5137 addLastSuccessfullAuthenticatedTimeColumn(db);
5138 oldVersion++;
5139 }
5140
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005141 if (oldVersion == 7) {
5142 addDebugTable(db);
5143 oldVersion++;
5144 }
5145
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005146 if (oldVersion == 8) {
5147 populateMetaTableWithAuthTypeAndUID(
5148 db,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005149 getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005150 oldVersion++;
5151 }
5152
Amith Yamasani67df64b2012-12-14 12:09:36 -08005153 if (oldVersion != newVersion) {
5154 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5155 }
Fred Quintana60307342009-03-24 22:48:12 -07005156 }
5157
5158 @Override
5159 public void onOpen(SQLiteDatabase db) {
5160 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
5161 }
5162 }
5163
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005164 static class DeDatabaseHelper extends SQLiteOpenHelper {
5165
5166 private final int mUserId;
5167 private volatile boolean mCeAttached;
5168
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005169 private DeDatabaseHelper(Context context, int userId, String deDatabaseName) {
5170 super(context, deDatabaseName, null, DE_DATABASE_VERSION);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005171 mUserId = userId;
5172 }
5173
5174 /**
5175 * This call needs to be made while the mCacheLock is held. The way to
5176 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
5177 * @param db The database.
5178 */
5179 @Override
5180 public void onCreate(SQLiteDatabase db) {
5181 Log.i(TAG, "Creating DE database for user " + mUserId);
5182 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
5183 + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
5184 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5185 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5186 + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
5187 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
5188 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5189
5190 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
5191 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
5192 + META_VALUE + " TEXT)");
5193
5194 createGrantsTable(db);
5195 createSharedAccountsTable(db);
5196 createAccountsDeletionTrigger(db);
5197 DebugDbHelper.createDebugTable(db);
5198 }
5199
5200 private void createSharedAccountsTable(SQLiteDatabase db) {
5201 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
5202 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5203 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5204 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5205 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5206 }
5207
5208 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5209 db.execSQL(""
5210 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5211 + " BEGIN"
5212 + " DELETE FROM " + TABLE_GRANTS
5213 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5214 + " END");
5215 }
5216
5217 private void createGrantsTable(SQLiteDatabase db) {
5218 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
5219 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5220 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
5221 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
5222 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
5223 + "," + GRANTS_GRANTEE_UID + "))");
5224 }
5225
5226 @Override
5227 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5228 Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
5229
5230 if (oldVersion != newVersion) {
5231 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5232 }
5233 }
5234
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005235 public void attachCeDatabase(File ceDbFile) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005236 SQLiteDatabase db = getWritableDatabase();
5237 db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb");
5238 mCeAttached = true;
5239 }
5240
5241 public boolean isCeDatabaseAttached() {
5242 return mCeAttached;
5243 }
5244
5245
5246 public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
5247 if(!mCeAttached) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005248 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
5249 + " is still locked. CE database is not yet available.", new Throwable());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005250 }
5251 return super.getReadableDatabase();
5252 }
5253
5254 public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
5255 if(!mCeAttached) {
5256 Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005257 + " is still locked. CE database is not yet available.", new Throwable());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005258 }
5259 return super.getWritableDatabase();
5260 }
5261
5262 @Override
5263 public void onOpen(SQLiteDatabase db) {
5264 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
5265 }
5266
5267 private void migratePreNDbToDe(File preNDbFile) {
5268 Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
5269 SQLiteDatabase db = getWritableDatabase();
5270 db.execSQL("ATTACH DATABASE '" + preNDbFile.getPath() + "' AS preNDb");
5271 db.beginTransaction();
5272 // Copy accounts fields
5273 db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
5274 + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
5275 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
5276 + ") "
5277 + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
5278 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
5279 + " FROM preNDb." + TABLE_ACCOUNTS);
5280 // Copy SHARED_ACCOUNTS
5281 db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
5282 + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
5283 "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
5284 + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
5285 // Copy DEBUG_TABLE
5286 db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
5287 + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
5288 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
5289 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
5290 "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
5291 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
5292 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
5293 + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
5294 // Copy GRANTS
5295 db.execSQL("INSERT INTO " + TABLE_GRANTS
5296 + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
5297 + GRANTS_GRANTEE_UID + ") " +
5298 "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
5299 + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
5300 // Copy META
5301 db.execSQL("INSERT INTO " + TABLE_META
5302 + "(" + META_KEY + "," + META_VALUE + ") "
5303 + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
5304 db.setTransactionSuccessful();
5305 db.endTransaction();
5306
5307 db.execSQL("DETACH DATABASE preNDb");
5308 }
5309
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005310 static DeDatabaseHelper create(
5311 Context context,
5312 int userId,
5313 File preNDatabaseFile,
5314 File deDatabaseFile) {
5315 boolean newDbExists = deDatabaseFile.exists();
5316 DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
5317 deDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005318 // If the db just created, and there is a legacy db, migrate it
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005319 if (!newDbExists && preNDatabaseFile.exists()) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005320 // Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005321 PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
5322 preNDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005323 // Open the database to force upgrade if required
5324 preNDatabaseHelper.getWritableDatabase();
5325 preNDatabaseHelper.close();
5326 // Move data without SPII to DE
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005327 deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005328 }
5329 return deDatabaseHelper;
5330 }
5331 }
5332
5333 static class CeDatabaseHelper extends SQLiteOpenHelper {
5334
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005335 public CeDatabaseHelper(Context context, String ceDatabaseName) {
5336 super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005337 }
5338
5339 /**
5340 * This call needs to be made while the mCacheLock is held.
5341 * @param db The database.
5342 */
5343 @Override
5344 public void onCreate(SQLiteDatabase db) {
5345 Log.i(TAG, "Creating CE database " + getDatabaseName());
5346 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
5347 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5348 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5349 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5350 + ACCOUNTS_PASSWORD + " TEXT, "
5351 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5352
5353 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
5354 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5355 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5356 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
5357 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
5358 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
5359
5360 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
5361 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5362 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
5363 + EXTRAS_KEY + " TEXT NOT NULL, "
5364 + EXTRAS_VALUE + " TEXT, "
5365 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
5366
5367 createAccountsDeletionTrigger(db);
5368 }
5369
5370 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5371 db.execSQL(""
5372 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5373 + " BEGIN"
5374 + " DELETE FROM " + TABLE_AUTHTOKENS
5375 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5376 + " DELETE FROM " + TABLE_EXTRAS
5377 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5378 + " END");
5379 }
5380
5381 @Override
5382 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5383 Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
5384
5385 if (oldVersion == 9) {
5386 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5387 Log.v(TAG, "onUpgrade upgrading to v10");
5388 }
5389 db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
5390 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
5391 // Recreate the trigger, since the old one references the table to be removed
5392 db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
5393 createAccountsDeletionTrigger(db);
5394 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
5395 db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
5396 oldVersion ++;
5397 }
5398
5399 if (oldVersion != newVersion) {
5400 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5401 }
5402 }
5403
5404 @Override
5405 public void onOpen(SQLiteDatabase db) {
5406 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
5407 }
5408
Fyodor Kupolov35f68082016-04-06 12:14:17 -07005409
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005410 /**
5411 * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
5412 * it also performs migration to the new CE database.
5413 * @param context
5414 * @param userId id of the user where the database is located
5415 */
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005416 static CeDatabaseHelper create(
5417 Context context,
5418 int userId,
5419 File preNDatabaseFile,
5420 File ceDatabaseFile) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005421 boolean newDbExists = ceDatabaseFile.exists();
5422 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5423 Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005424 + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005425 }
5426 boolean removeOldDb = false;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005427 if (!newDbExists && preNDatabaseFile.exists()) {
5428 removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005429 }
5430 // Try to open and upgrade if necessary
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005431 CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005432 ceHelper.getWritableDatabase();
5433 ceHelper.close();
5434 if (removeOldDb) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005435 Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile);
5436 if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) {
5437 Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile);
5438 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005439 }
5440 return ceHelper;
5441 }
5442
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005443 private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005444 Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005445 try {
5446 FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
5447 } catch (IOException e) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005448 Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005449 // Try to remove potentially damaged file if I/O error occurred
5450 deleteDbFileWarnIfFailed(ceDbFile);
5451 return false;
5452 }
5453 return true;
5454 }
5455 }
5456
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005457 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07005458 return asBinder();
5459 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005460
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005461 /**
5462 * Searches array of arguments for the specified string
5463 * @param args array of argument strings
5464 * @param value value to search for
5465 * @return true if the value is contained in the array
5466 */
5467 private static boolean scanArgs(String[] args, String value) {
5468 if (args != null) {
5469 for (String arg : args) {
5470 if (value.equals(arg)) {
5471 return true;
5472 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005473 }
5474 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005475 return false;
5476 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005477
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005478 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005479 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07005480 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
5481 != PackageManager.PERMISSION_GRANTED) {
5482 fout.println("Permission Denial: can't dump AccountsManager from from pid="
5483 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
5484 + " without permission " + android.Manifest.permission.DUMP);
5485 return;
5486 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005487 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005488 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07005489
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005490 final List<UserInfo> users = getUserManager().getUsers();
5491 for (UserInfo user : users) {
5492 ipw.println("User " + user + ":");
5493 ipw.increaseIndent();
5494 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
5495 ipw.println();
5496 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08005497 }
5498 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005499
Amith Yamasani04e0d262012-02-14 11:50:53 -08005500 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
5501 String[] args, boolean isCheckinRequest) {
5502 synchronized (userAccounts.cacheLock) {
5503 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005504
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005505 if (isCheckinRequest) {
5506 // This is a checkin request. *Only* upload the account types and the count of each.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005507 AccountsDbUtils.dumpAccountsTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005508 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005509 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07005510 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005511 fout.println("Accounts: " + accounts.length);
5512 for (Account account : accounts) {
5513 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005514 }
Fred Quintana307da1a2010-01-21 14:24:20 -08005515
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005516 // Add debug information.
5517 fout.println();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005518 AccountsDbUtils.dumpDebugTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005519 fout.println();
5520 synchronized (mSessions) {
5521 final long now = SystemClock.elapsedRealtime();
5522 fout.println("Active Sessions: " + mSessions.size());
5523 for (Session session : mSessions.values()) {
5524 fout.println(" " + session.toDebugString(now));
5525 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005526 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005527
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005528 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005529 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005530 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005531 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005532 }
5533
Amith Yamasani04e0d262012-02-14 11:50:53 -08005534 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005535 Intent intent, String packageName, final int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005536 long identityToken = clearCallingIdentity();
5537 try {
5538 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5539 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
5540 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005541
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005542 if (intent.getComponent() != null &&
5543 GrantCredentialsPermissionActivity.class.getName().equals(
5544 intent.getComponent().getClassName())) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005545 createNoCredentialsPermissionNotification(account, intent, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005546 } else {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005547 Context contextForUser = getContextForUser(new UserHandle(userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005548 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07005549 intent.addCategory(String.valueOf(notificationId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005550
Fred Quintana33f889a2009-09-14 17:31:26 -07005551 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01005552 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04005553 Notification n = new Notification.Builder(contextForUser)
5554 .setWhen(0)
5555 .setSmallIcon(android.R.drawable.stat_sys_warning)
5556 .setColor(contextForUser.getColor(
5557 com.android.internal.R.color.system_notification_accent_color))
5558 .setContentTitle(String.format(notificationTitleFormat, account.name))
5559 .setContentText(message)
5560 .setContentIntent(PendingIntent.getActivityAsUser(
5561 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005562 null, new UserHandle(userId)))
Chris Wren1ce4b6d2015-06-11 10:19:43 -04005563 .build();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005564 installNotification(notificationId, n, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005565 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005566 } finally {
5567 restoreCallingIdentity(identityToken);
5568 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005569 }
5570
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005571 @VisibleForTesting
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005572 protected void installNotification(int notificationId, final Notification notification,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005573 UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005574 installNotification(notificationId, notification, "android", user.getIdentifier());
5575 }
5576
5577 private void installNotification(int notificationId, final Notification notification,
5578 String packageName, int userId) {
5579 final long token = clearCallingIdentity();
5580 try {
5581 INotificationManager notificationManager = NotificationManager.getService();
5582 try {
5583 notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
5584 notificationId, notification, new int[1], userId);
5585 } catch (RemoteException e) {
5586 /* ignore - local call */
5587 }
5588 } finally {
5589 Binder.restoreCallingIdentity(token);
5590 }
Fred Quintana56285a62010-12-02 14:20:51 -08005591 }
5592
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005593 @VisibleForTesting
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005594 protected void cancelNotification(int id, UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005595 cancelNotification(id, mContext.getPackageName(), user);
5596 }
5597
5598 protected void cancelNotification(int id, String packageName, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005599 long identityToken = clearCallingIdentity();
5600 try {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005601 INotificationManager service = INotificationManager.Stub.asInterface(
5602 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
5603 service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
5604 } catch (RemoteException e) {
5605 /* ignore - local call */
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005606 } finally {
5607 restoreCallingIdentity(identityToken);
5608 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005609 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005610
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005611 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
5612 for (String perm : permissions) {
5613 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
5614 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5615 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
5616 }
5617 final int opCode = AppOpsManager.permissionToOpCode(perm);
5618 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
5619 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
5620 return true;
5621 }
5622 }
5623 }
5624 return false;
5625 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005626
Amith Yamasani67df64b2012-12-14 12:09:36 -08005627 private int handleIncomingUser(int userId) {
5628 try {
5629 return ActivityManagerNative.getDefault().handleIncomingUser(
5630 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
5631 } catch (RemoteException re) {
5632 // Shouldn't happen, local.
5633 }
5634 return userId;
5635 }
5636
Christopher Tateccbf84f2013-05-08 15:25:41 -07005637 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005638 final int callingUserId = UserHandle.getUserId(callingUid);
5639
5640 final PackageManager userPackageManager;
5641 try {
5642 userPackageManager = mContext.createPackageContextAsUser(
5643 "android", 0, new UserHandle(callingUserId)).getPackageManager();
5644 } catch (NameNotFoundException e) {
5645 return false;
5646 }
5647
5648 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07005649 for (String name : packages) {
5650 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005651 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08005652 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08005653 && (packageInfo.applicationInfo.privateFlags
5654 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07005655 return true;
5656 }
5657 } catch (PackageManager.NameNotFoundException e) {
5658 return false;
5659 }
5660 }
5661 return false;
5662 }
5663
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005664 private boolean permissionIsGranted(
5665 Account account, String authTokenType, int callerUid, int userId) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005666 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5667 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5668 Log.v(TAG, "Access to " + account + " granted calling uid is system");
5669 }
5670 return true;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005671 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005672
5673 if (isPrivileged(callerUid)) {
5674 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5675 Log.v(TAG, "Access to " + account + " granted calling uid "
5676 + callerUid + " privileged");
5677 }
5678 return true;
5679 }
5680 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
5681 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5682 Log.v(TAG, "Access to " + account + " granted calling uid "
5683 + callerUid + " manages the account");
5684 }
5685 return true;
5686 }
5687 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5688 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5689 Log.v(TAG, "Access to " + account + " granted calling uid "
5690 + callerUid + " user granted access");
5691 }
5692 return true;
5693 }
5694
5695 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5696 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5697 }
5698
5699 return false;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005700 }
5701
Svetoslavf3f02ac2015-09-08 14:36:35 -07005702 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5703 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005704 if (accountType == null) {
5705 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005706 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07005707 return getTypesVisibleToCaller(callingUid, userId,
5708 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005709 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005710 }
5711
5712 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5713 if (accountType == null) {
5714 return false;
5715 } else {
5716 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5717 }
5718 }
5719
Svetoslavf3f02ac2015-09-08 14:36:35 -07005720 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5721 String opPackageName) {
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005722 boolean isPermitted =
5723 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5724 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005725 return getTypesForCaller(callingUid, userId, isPermitted);
5726 }
5727
5728 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5729 return getTypesForCaller(callingUid, userId, false);
5730 }
5731
5732 private List<String> getTypesForCaller(
5733 int callingUid, int userId, boolean isOtherwisePermitted) {
5734 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005735 long identityToken = Binder.clearCallingIdentity();
5736 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5737 try {
5738 serviceInfos = mAuthenticatorCache.getAllServices(userId);
5739 } finally {
5740 Binder.restoreCallingIdentity(identityToken);
5741 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005742 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005743 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005744 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5745 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5746 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005747 }
5748 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005749 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005750 }
5751
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07005752 private boolean isAccountPresentForCaller(String accountName, String accountType) {
5753 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5754 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5755 if (account.name.equals(accountName)) {
5756 return true;
5757 }
5758 }
5759 }
5760 return false;
5761 }
5762
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07005763 private static void checkManageUsersPermission(String message) {
5764 if (ActivityManager.checkComponentPermission(
5765 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5766 != PackageManager.PERMISSION_GRANTED) {
5767 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5768 }
5769 }
5770
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07005771 private static void checkManageOrCreateUsersPermission(String message) {
5772 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5773 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5774 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5775 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5776 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5777 + message);
5778 }
5779 }
5780
Amith Yamasani04e0d262012-02-14 11:50:53 -08005781 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5782 int callerUid) {
Amith Yamasani27db4682013-03-30 17:07:47 -07005783 if (callerUid == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005784 return true;
5785 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005786 UserAccounts accounts = getUserAccountsForCaller();
5787 synchronized (accounts.cacheLock) {
5788 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005789 final String query;
5790 final String[] args;
5791
5792 if (authTokenType != null) {
5793 query = COUNT_OF_MATCHING_GRANTS;
5794 args = new String[] {String.valueOf(callerUid), authTokenType,
5795 account.name, account.type};
5796 } else {
5797 query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN;
5798 args = new String[] {String.valueOf(callerUid), account.name,
5799 account.type};
5800 }
5801 final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0;
Svet Ganov890a2102016-08-24 00:08:00 -07005802
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005803 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5804 // TODO: Skip this check when running automated tests. Replace this
5805 // with a more general solution.
5806 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08005807 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005808 + " but ignoring since device is in test harness.");
5809 return true;
5810 }
5811 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005812 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005813 }
5814
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005815 private boolean isSystemUid(int callingUid) {
5816 String[] packages = null;
5817 long ident = Binder.clearCallingIdentity();
5818 try {
5819 packages = mPackageManager.getPackagesForUid(callingUid);
5820 } finally {
5821 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005822 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005823 if (packages != null) {
5824 for (String name : packages) {
5825 try {
5826 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5827 if (packageInfo != null
5828 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5829 != 0) {
5830 return true;
5831 }
5832 } catch (PackageManager.NameNotFoundException e) {
5833 Log.w(TAG, String.format("Could not find package [%s]", name), e);
5834 }
5835 }
5836 } else {
5837 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005838 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005839 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00005840 }
5841
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005842 /** Succeeds if any of the specified permissions are granted. */
5843 private void checkReadAccountsPermitted(
5844 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005845 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07005846 int userId,
5847 String opPackageName) {
5848 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005849 String msg = String.format(
5850 "caller uid %s cannot access %s accounts",
5851 callingUid,
5852 accountType);
5853 Log.w(TAG, " " + msg);
5854 throw new SecurityException(msg);
5855 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005856 }
5857
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005858 private boolean canUserModifyAccounts(int userId, int callingUid) {
5859 // the managing app can always modify accounts
5860 if (isProfileOwner(callingUid)) {
5861 return true;
5862 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005863 if (getUserManager().getUserRestrictions(new UserHandle(userId))
5864 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5865 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005866 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005867 return true;
5868 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005869
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005870 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5871 // the managing app can always modify accounts
5872 if (isProfileOwner(callingUid)) {
5873 return true;
5874 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005875 DevicePolicyManager dpm = (DevicePolicyManager) mContext
5876 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005877 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02005878 if (typesArray == null) {
5879 return true;
5880 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005881 for (String forbiddenType : typesArray) {
5882 if (forbiddenType.equals(accountType)) {
5883 return false;
5884 }
5885 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005886 return true;
5887 }
5888
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005889 private boolean isProfileOwner(int uid) {
5890 final DevicePolicyManagerInternal dpmi =
5891 LocalServices.getService(DevicePolicyManagerInternal.class);
5892 return (dpmi != null)
5893 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5894 }
5895
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08005896 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07005897 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5898 throws RemoteException {
5899 final int callingUid = getCallingUid();
5900
Amith Yamasani27db4682013-03-30 17:07:47 -07005901 if (callingUid != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07005902 throw new SecurityException();
5903 }
5904
5905 if (value) {
5906 grantAppPermission(account, authTokenType, uid);
5907 } else {
5908 revokeAppPermission(account, authTokenType, uid);
5909 }
5910 }
5911
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005912 /**
5913 * Allow callers with the given uid permission to get credentials for account/authTokenType.
5914 * <p>
5915 * Although this is public it can only be accessed via the AccountManagerService object
5916 * which is in the system. This means we don't need to protect it with permissions.
5917 * @hide
5918 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005919 private void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005920 if (account == null || authTokenType == null) {
5921 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005922 return;
5923 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005924 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005925 synchronized (accounts.cacheLock) {
5926 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005927 long accountId = AccountsDbUtils.findAccountId(db, account);
5928 if (accountId >= 0) {
5929 AccountsDbUtils.insertGrant(db, accountId, authTokenType, uid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005930 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005931 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005932 UserHandle.of(accounts.userId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005933
5934 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005935 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005936 }
5937
5938 /**
5939 * Don't allow callers with the given uid permission to get credentials for
5940 * account/authTokenType.
5941 * <p>
5942 * Although this is public it can only be accessed via the AccountManagerService object
5943 * which is in the system. This means we don't need to protect it with permissions.
5944 * @hide
5945 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005946 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005947 if (account == null || authTokenType == null) {
5948 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005949 return;
5950 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005951 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005952 synchronized (accounts.cacheLock) {
5953 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005954 db.beginTransaction();
5955 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005956 long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005957 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005958 AccountsDbUtils.deleteGrantsByAccountIdAuthTokenTypeAndUid(
5959 db, accountId, authTokenType, uid);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005960 db.setTransactionSuccessful();
5961 }
5962 } finally {
5963 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005964 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005965 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5966 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005967 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005968 }
Fred Quintana56285a62010-12-02 14:20:51 -08005969
5970 static final private String stringArrayToString(String[] value) {
5971 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
5972 }
5973
Amith Yamasani04e0d262012-02-14 11:50:53 -08005974 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
5975 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005976 if (oldAccountsForType != null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07005977 ArrayList<Account> newAccountsList = new ArrayList<>();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005978 for (Account curAccount : oldAccountsForType) {
5979 if (!curAccount.equals(account)) {
5980 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08005981 }
5982 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005983 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08005984 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005985 } else {
5986 Account[] newAccountsForType = new Account[newAccountsList.size()];
5987 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08005988 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005989 }
Fred Quintana56285a62010-12-02 14:20:51 -08005990 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005991 accounts.userDataCache.remove(account);
5992 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005993 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08005994 }
5995
5996 /**
5997 * This assumes that the caller has already checked that the account is not already present.
5998 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08005999 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
6000 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006001 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
6002 Account[] newAccountsForType = new Account[oldLength + 1];
6003 if (accountsForType != null) {
6004 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08006005 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006006 newAccountsForType[oldLength] = account;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006007 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08006008 }
6009
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006010 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07006011 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006012 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07006013 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006014 return unfiltered;
6015 }
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07006016 UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
Amith Yamasani0c19bf52013-10-03 10:34:58 -07006017 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006018 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006019 // If any of the packages is a visible listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006020 // otherwise return non-shared accounts only.
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006021 // This might be a temporary way to specify a visible list
6022 String visibleList = mContext.getResources().getString(
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006023 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
6024 for (String packageName : packages) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006025 if (visibleList.contains(";" + packageName + ";")) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006026 return unfiltered;
6027 }
6028 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006029 ArrayList<Account> allowed = new ArrayList<>();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006030 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
6031 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006032 String requiredAccountType = "";
6033 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07006034 // If there's an explicit callingPackage specified, check if that package
6035 // opted in to see restricted accounts.
6036 if (callingPackage != null) {
6037 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006038 if (pi != null && pi.restrictedAccountType != null) {
6039 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07006040 }
6041 } else {
6042 // Otherwise check if the callingUid has a package that has opted in
6043 for (String packageName : packages) {
6044 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
6045 if (pi != null && pi.restrictedAccountType != null) {
6046 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07006047 break;
6048 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006049 }
6050 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006051 } catch (NameNotFoundException nnfe) {
6052 }
6053 for (Account account : unfiltered) {
6054 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006055 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006056 } else {
6057 boolean found = false;
6058 for (Account shared : sharedAccounts) {
6059 if (shared.equals(account)) {
6060 found = true;
6061 break;
6062 }
6063 }
6064 if (!found) {
6065 allowed.add(account);
6066 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006067 }
6068 }
6069 Account[] filtered = new Account[allowed.size()];
6070 allowed.toArray(filtered);
6071 return filtered;
6072 } else {
6073 return unfiltered;
6074 }
6075 }
6076
Amith Yamasani27db4682013-03-30 17:07:47 -07006077 /*
6078 * packageName can be null. If not null, it should be used to filter out restricted accounts
6079 * that the package is not allowed to access.
6080 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006081 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07006082 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006083 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08006084 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006085 if (accounts == null) {
6086 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08006087 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006088 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07006089 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08006090 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006091 } else {
6092 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006093 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006094 totalLength += accounts.length;
6095 }
6096 if (totalLength == 0) {
6097 return EMPTY_ACCOUNT_ARRAY;
6098 }
6099 Account[] accounts = new Account[totalLength];
6100 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006101 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006102 System.arraycopy(accountsOfType, 0, accounts, totalLength,
6103 accountsOfType.length);
6104 totalLength += accountsOfType.length;
6105 }
Amith Yamasani27db4682013-03-30 17:07:47 -07006106 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08006107 }
6108 }
6109
Amith Yamasani04e0d262012-02-14 11:50:53 -08006110 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
6111 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006112 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006113 if (userDataForAccount == null) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006114 userDataForAccount = AccountsDbUtils.findUserExtrasForAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006115 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006116 }
6117 if (value == null) {
6118 userDataForAccount.remove(key);
6119 } else {
6120 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08006121 }
6122 }
6123
Carlos Valdivia91979be2015-05-22 14:11:35 -07006124 protected String readCachedTokenInternal(
6125 UserAccounts accounts,
6126 Account account,
6127 String tokenType,
6128 String callingPackage,
6129 byte[] pkgSigDigest) {
6130 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07006131 return accounts.accountTokenCaches.get(
6132 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07006133 }
6134 }
6135
Amith Yamasani04e0d262012-02-14 11:50:53 -08006136 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
6137 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006138 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006139 if (authTokensForAccount == null) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006140 authTokensForAccount = AccountsDbUtils.findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006141 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006142 }
6143 if (value == null) {
6144 authTokensForAccount.remove(key);
6145 } else {
6146 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08006147 }
6148 }
6149
Amith Yamasani04e0d262012-02-14 11:50:53 -08006150 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
6151 String authTokenType) {
6152 synchronized (accounts.cacheLock) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006153 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08006154 if (authTokensForAccount == null) {
6155 // need to populate the cache for this account
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07006156 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006157 authTokensForAccount = AccountsDbUtils
6158 .findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006159 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006160 }
6161 return authTokensForAccount.get(authTokenType);
6162 }
6163 }
6164
Simranjit Kohli858511c2016-03-10 18:36:11 +00006165 protected String readUserDataInternalLocked(
6166 UserAccounts accounts, Account account, String key) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006167 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00006168 if (userDataForAccount == null) {
6169 // need to populate the cache for this account
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07006170 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006171 userDataForAccount = AccountsDbUtils.findUserExtrasForAccount(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00006172 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006173 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00006174 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08006175 }
6176
Kenny Guy07ad8dc2014-09-01 20:56:12 +01006177 private Context getContextForUser(UserHandle user) {
6178 try {
6179 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
6180 } catch (NameNotFoundException e) {
6181 // Default to mContext, not finding the package system is running as is unlikely.
6182 return mContext;
6183 }
6184 }
Sandra Kwan78812282015-11-04 11:19:47 -08006185
6186 private void sendResponse(IAccountManagerResponse response, Bundle result) {
6187 try {
6188 response.onResult(result);
6189 } catch (RemoteException e) {
6190 // if the caller is dead then there is no one to care about remote
6191 // exceptions
6192 if (Log.isLoggable(TAG, Log.VERBOSE)) {
6193 Log.v(TAG, "failure while notifying response", e);
6194 }
6195 }
6196 }
6197
6198 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
6199 String errorMessage) {
6200 try {
6201 response.onError(errorCode, errorMessage);
6202 } catch (RemoteException e) {
6203 // if the caller is dead then there is no one to care about remote
6204 // exceptions
6205 if (Log.isLoggable(TAG, Log.VERBOSE)) {
6206 Log.v(TAG, "failure while notifying response", e);
6207 }
6208 }
6209 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006210
6211 static class AccountsDbUtils {
6212
6213 static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
6214 String type) {
6215 Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
6216 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
6217 new String[]{name, type}, null, null, null);
6218 try {
6219 if (cursor.moveToNext()) {
6220 return cursor.getString(0);
6221 }
6222 return null;
6223 } finally {
6224 cursor.close();
6225 }
6226 }
6227
6228 static Map<Long, Account> findAllAccounts(SQLiteDatabase db) {
6229 LinkedHashMap<Long, Account> map = new LinkedHashMap<>();
6230 Cursor cursor = db.query(TABLE_ACCOUNTS,
6231 new String[] {ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
6232 null, null, null, null, ACCOUNTS_ID);
6233 try {
6234 while (cursor.moveToNext()) {
6235 final long accountId = cursor.getLong(0);
6236 final String accountType = cursor.getString(1);
6237 final String accountName = cursor.getString(2);
6238
6239 final Account account = new Account(accountName, accountType);
6240 map.put(accountId, account);
6241 }
6242 } finally {
6243 cursor.close();
6244 }
6245 return map;
6246 }
6247
6248 static String findAccountPreviousName(SQLiteDatabase db, Account account) {
6249 Cursor cursor = db.query(
6250 TABLE_ACCOUNTS,
6251 new String[]{ ACCOUNTS_PREVIOUS_NAME },
6252 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
6253 new String[] { account.name, account.type },
6254 null,
6255 null,
6256 null);
6257 try {
6258 if (cursor.moveToNext()) {
6259 return cursor.getString(0);
6260 }
6261 } finally {
6262 cursor.close();
6263 }
6264 return null;
6265 }
6266
6267 static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
6268 // Select accounts from CE that do not exist in DE
6269 Cursor cursor = db.rawQuery(
6270 "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
6271 + " FROM " + CE_TABLE_ACCOUNTS
6272 + " WHERE NOT EXISTS "
6273 + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS
6274 + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
6275 + " )", null);
6276 try {
6277 List<Account> accounts = new ArrayList<>(cursor.getCount());
6278 while (cursor.moveToNext()) {
6279 String accountName = cursor.getString(0);
6280 String accountType = cursor.getString(1);
6281 accounts.add(new Account(accountName, accountType));
6282 }
6283 return accounts;
6284 } finally {
6285 cursor.close();
6286 }
6287 }
6288
6289 static boolean deleteAccount(SQLiteDatabase db, long accountId) {
6290 return db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
6291 }
6292
6293 static boolean deleteCeAccount(SQLiteDatabase db, long accountId) {
6294 return db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
6295 }
6296
6297 /**
6298 * Returns information about auth tokens and their account for the specified query parameters.
6299 * Output is in the format:
6300 * <pre><code> | AUTHTOKEN_ID | ACCOUNT_NAME | AUTH_TOKEN_TYPE |</code></pre>
6301 */
6302 static Cursor findAuthtokenForAllAccounts(SQLiteDatabase db, String accountType,
6303 String authToken) {
6304 return db.rawQuery(
6305 "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
6306 + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
6307 + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
6308 + " FROM " + CE_TABLE_ACCOUNTS
6309 + " JOIN " + CE_TABLE_AUTHTOKENS
6310 + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
6311 + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
6312 + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN
6313 + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
6314 new String[]{authToken, accountType});
6315 }
6316
6317 static boolean deleteAuthtokensByAccountIdAndType(SQLiteDatabase db, long accountId,
6318 String authtokenType) {
6319 return db.delete(CE_TABLE_AUTHTOKENS,
6320 AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
6321 new String[] {String.valueOf(accountId), authtokenType}) > 0;
6322 }
6323
6324 static boolean deleteAuthToken(SQLiteDatabase db, String authTokenId) {
6325 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "= ?",
6326 new String[] {authTokenId}) > 0;
6327 }
6328
6329 static long insertAuthToken(SQLiteDatabase db, long accountId, String authTokenType,
6330 String authToken) {
6331 ContentValues values = new ContentValues();
6332 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
6333 values.put(AUTHTOKENS_TYPE, authTokenType);
6334 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
6335 return db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values);
6336 }
6337
6338 static Map<String, String> findAuthTokensByAccount(final SQLiteDatabase db, Account account) {
6339 HashMap<String, String> authTokensForAccount = new HashMap<>();
6340 Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
6341 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
6342 SELECTION_AUTHTOKENS_BY_ACCOUNT,
6343 new String[]{account.name, account.type},
6344 null, null, null);
6345 try {
6346 while (cursor.moveToNext()) {
6347 final String type = cursor.getString(0);
6348 final String authToken = cursor.getString(1);
6349 authTokensForAccount.put(type, authToken);
6350 }
6351 } finally {
6352 cursor.close();
6353 }
6354 return authTokensForAccount;
6355 }
6356
6357 static int updateAccountPassword(SQLiteDatabase db, long accountId, String password) {
6358 final ContentValues values = new ContentValues();
6359 values.put(ACCOUNTS_PASSWORD, password);
6360 return db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?",
6361 new String[] {String.valueOf(accountId)});
6362 }
6363
6364 static boolean deleteAuthTokensByAccountId(SQLiteDatabase db, long accountId) {
6365 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?",
6366 new String[] {String.valueOf(accountId)}) > 0;
6367 }
6368
6369 static long insertSharedAccount(SQLiteDatabase db, Account account) {
6370 ContentValues values = new ContentValues();
6371 values.put(ACCOUNTS_NAME, account.name);
6372 values.put(ACCOUNTS_TYPE, account.type);
6373 return db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
6374 }
6375
6376 static boolean deleteSharedAccount(SQLiteDatabase db, Account account) {
6377 return db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
6378 new String[] {account.name, account.type}) > 0;
6379 }
6380
6381 static int renameSharedAccount(SQLiteDatabase db, Account account, String newName) {
6382 final ContentValues values = new ContentValues();
6383 values.put(ACCOUNTS_NAME, newName);
6384 return db.update(TABLE_SHARED_ACCOUNTS,
6385 values,
6386 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
6387 new String[] { account.name, account.type });
6388 }
6389
6390 static List<Account> getSharedAccounts(SQLiteDatabase db) {
6391 ArrayList<Account> accountList = new ArrayList<>();
6392 Cursor cursor = null;
6393 try {
6394 cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE},
6395 null, null, null, null, null);
6396 if (cursor != null && cursor.moveToFirst()) {
6397 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
6398 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
6399 do {
6400 accountList.add(new Account(cursor.getString(nameIndex),
6401 cursor.getString(typeIndex)));
6402 } while (cursor.moveToNext());
6403 }
6404 } finally {
6405 if (cursor != null) {
6406 cursor.close();
6407 }
6408 }
6409 return accountList;
6410 }
6411
6412 static long findSharedAccountId(SQLiteDatabase db, Account account) {
6413 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
6414 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
6415 try {
6416 if (cursor.moveToNext()) {
6417 return cursor.getLong(0);
6418 }
6419 return -1;
6420 } finally {
6421 cursor.close();
6422 }
6423 }
6424
6425 static long findAccountId(SQLiteDatabase db, Account account) {
6426 Cursor cursor = db.query(
6427 TABLE_ACCOUNTS, new String[] {ACCOUNTS_ID},
6428 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
6429 try {
6430 if (cursor.moveToNext()) {
6431 return cursor.getLong(0);
6432 }
6433 return -1;
6434 } finally {
6435 cursor.close();
6436 }
6437 }
6438
6439 static long findExtrasIdByAccountId(SQLiteDatabase db, long accountId, String key) {
6440 Cursor cursor = db.query(
6441 CE_TABLE_EXTRAS, new String[] {EXTRAS_ID},
6442 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
6443 new String[]{key}, null, null, null);
6444 try {
6445 if (cursor.moveToNext()) {
6446 return cursor.getLong(0);
6447 }
6448 return -1;
6449 } finally {
6450 cursor.close();
6451 }
6452 }
6453
6454 static boolean updateExtra(SQLiteDatabase db, long extrasId, String value) {
6455 ContentValues values = new ContentValues();
6456 values.put(EXTRAS_VALUE, value);
6457 int rows = db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=?",
6458 new String[]{String.valueOf(extrasId)});
6459 return rows == 1;
6460 }
6461
6462 static long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
6463 ContentValues values = new ContentValues();
6464 values.put(EXTRAS_KEY, key);
6465 values.put(EXTRAS_ACCOUNTS_ID, accountId);
6466 values.put(EXTRAS_VALUE, value);
6467 return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
6468 }
6469
6470 static Map<String, String> findUserExtrasForAccount(SQLiteDatabase db, Account account) {
6471 Map<String, String> userExtrasForAccount = new HashMap<>();
6472 Cursor cursor = db.query(CE_TABLE_EXTRAS,
6473 COLUMNS_EXTRAS_KEY_AND_VALUE,
6474 SELECTION_USERDATA_BY_ACCOUNT,
6475 new String[] {account.name, account.type},
6476 null, null, null);
6477 try {
6478 while (cursor.moveToNext()) {
6479 final String tmpkey = cursor.getString(0);
6480 final String value = cursor.getString(1);
6481 userExtrasForAccount.put(tmpkey, value);
6482 }
6483 } finally {
6484 cursor.close();
6485 }
6486 return userExtrasForAccount;
6487 }
6488
6489 static long insertGrant(SQLiteDatabase db, long accountId, String authTokenType, int uid) {
6490 ContentValues values = new ContentValues();
6491 values.put(GRANTS_ACCOUNTS_ID, accountId);
6492 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
6493 values.put(GRANTS_GRANTEE_UID, uid);
6494 return db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
6495 }
6496
6497 static boolean deleteGrantsByUid(SQLiteDatabase db, int uid) {
6498 return db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
6499 new String[] {Integer.toString(uid)}) > 0;
6500 }
6501
6502 static boolean deleteGrantsByAccountIdAuthTokenTypeAndUid(SQLiteDatabase db, long accountId, String authTokenType, long uid) {
6503 return db.delete(TABLE_GRANTS,
6504 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
6505 + GRANTS_GRANTEE_UID + "=?",
6506 new String[] {String.valueOf(accountId), authTokenType, String.valueOf(uid)}) > 0;
6507 }
6508
6509 static List<Integer> findAllUidGrants(SQLiteDatabase db) {
6510 List<Integer> result = new ArrayList<>();
6511 final Cursor cursor = db.query(TABLE_GRANTS,
6512 new String[] {GRANTS_GRANTEE_UID},
6513 null, null, GRANTS_GRANTEE_UID, null, null);
6514 try {
6515 while (cursor.moveToNext()) {
6516 final int uid = cursor.getInt(0);
6517 result.add(uid);
6518 }
6519 } finally {
6520 cursor.close();
6521 }
6522 return result;
6523 }
6524
6525 static long findMatchingGrantsCount(SQLiteDatabase db,
6526 int uid, String authTokenType, Account account) {
6527 String[] args = { String.valueOf(uid), authTokenType,
6528 account.name, account.type};
6529 return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args);
6530 }
6531
6532 static long insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid) {
6533 ContentValues values = new ContentValues();
6534 values.put(META_KEY,
6535 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
6536 values.put(META_VALUE, uid);
6537 return db.insert(TABLE_META, null, values);
6538 }
6539
6540 static long insertOrReplaceMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType,
6541 int uid) {
6542 ContentValues values = new ContentValues();
6543 values.put(META_KEY,
6544 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
6545 values.put(META_VALUE, uid);
6546 return db.insertWithOnConflict(TABLE_META, null, values,
6547 SQLiteDatabase.CONFLICT_REPLACE);
6548 }
6549
6550
6551 static Map<String, Integer> findMetaAuthUid(SQLiteDatabase db) {
6552 Cursor metaCursor = db.query(
6553 TABLE_META,
6554 new String[] {META_KEY, META_VALUE},
6555 SELECTION_META_BY_AUTHENTICATOR_TYPE,
6556 new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
6557 null /* groupBy */,
6558 null /* having */,
6559 META_KEY);
6560 Map<String, Integer> map = new LinkedHashMap<>();
6561 try {
6562 while (metaCursor.moveToNext()) {
6563 String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
6564 String uidStr = metaCursor.getString(1);
6565 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uidStr)) {
6566 // Should never happen.
6567 Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
6568 + ", uid empty: " + TextUtils.isEmpty(uidStr));
6569 continue;
6570 }
6571 int uid = Integer.parseInt(metaCursor.getString(1));
6572 map.put(type, uid);
6573 }
6574 } finally {
6575 metaCursor.close();
6576 }
6577 return map;
6578 }
6579
6580 static boolean deleteMetaByAuthTypeAndUid(SQLiteDatabase db, String type, int uid) {
6581 return db.delete(
6582 TABLE_META,
6583 META_KEY + "=? AND " + META_VALUE + "=?",
6584 new String[] {
6585 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
6586 String.valueOf(uid)}
6587 ) > 0;
6588 }
6589
6590 static void dumpAccountsTable(SQLiteDatabase db, PrintWriter pw) {
6591 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
6592 null, null, ACCOUNTS_TYPE, null, null);
6593 try {
6594 while (cursor.moveToNext()) {
6595 // print type,count
6596 pw.println(cursor.getString(0) + "," + cursor.getString(1));
6597 }
6598 } finally {
6599 if (cursor != null) {
6600 cursor.close();
6601 }
6602 }
6603 }
6604
6605 static void dumpDebugTable(SQLiteDatabase db, PrintWriter pw) {
6606 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
6607 null, null, null, null, DebugDbHelper.TIMESTAMP);
6608 pw.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
6609 pw.println("Accounts History");
6610 try {
6611 while (cursor.moveToNext()) {
6612 // print type,count
6613 pw.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
6614 cursor.getString(2) + "," + cursor.getString(3) + ","
6615 + cursor.getString(4) + "," + cursor.getString(5));
6616 }
6617 } finally {
6618 cursor.close();
6619 }
6620 }
Svet Ganov890a2102016-08-24 00:08:00 -07006621 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006622
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006623 private final class AccountManagerInternalImpl extends AccountManagerInternal {
6624 @Override
6625 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
6626 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
6627 if (account == null) {
6628 Slog.w(TAG, "account cannot be null");
6629 return;
6630 }
6631 if (packageName == null) {
6632 Slog.w(TAG, "packageName cannot be null");
6633 return;
6634 }
6635 if (userId < UserHandle.USER_SYSTEM) {
6636 Slog.w(TAG, "user id must be concrete");
6637 return;
6638 }
6639 if (callback == null) {
6640 Slog.w(TAG, "callback cannot be null");
6641 return;
6642 }
6643
6644 if (hasAccountAccess(account, packageName, new UserHandle(userId))) {
6645 Bundle result = new Bundle();
6646 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
6647 callback.sendResult(result);
6648 return;
6649 }
6650
6651 final int uid;
6652 try {
6653 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
6654 } catch (NameNotFoundException e) {
6655 Slog.e(TAG, "Unknown package " + packageName);
6656 return;
6657 }
6658
6659 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
6660 doNotification(mUsers.get(userId), account, null, intent, packageName, userId);
6661 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006662 }
Fred Quintana60307342009-03-24 22:48:12 -07006663}