blob: a72e3ab6ad62ae446de4c8bdb14afb1007b26b06 [file] [log] [blame]
Fred Quintana60307342009-03-24 22:48:12 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jeff Sharkey7a96c392012-11-15 14:01:46 -080017package com.android.server.accounts;
Fred Quintana60307342009-03-24 22:48:12 -070018
Doug Zongker885cfc232009-10-21 16:52:44 -070019import android.Manifest;
Carlos Valdivia91979be2015-05-22 14:11:35 -070020import android.accounts.AbstractAccountAuthenticator;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080021import android.accounts.Account;
22import android.accounts.AccountAndUser;
23import android.accounts.AccountAuthenticatorResponse;
24import android.accounts.AccountManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070025import android.accounts.AccountManagerInternal;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080026import android.accounts.AuthenticatorDescription;
Amith Yamasani23c8b962013-04-10 13:37:18 -070027import android.accounts.CantAddAccountActivity;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080028import android.accounts.GrantCredentialsPermissionActivity;
Svet Ganovf6d424f12016-09-20 20:18:53 -070029import android.accounts.IAccountAccessTracker;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080030import android.accounts.IAccountAuthenticator;
31import android.accounts.IAccountAuthenticatorResponse;
32import android.accounts.IAccountManager;
33import android.accounts.IAccountManagerResponse;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070034import android.annotation.IntRange;
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -070035import android.annotation.NonNull;
Svet Ganovf6d424f12016-09-20 20:18:53 -070036import android.annotation.Nullable;
Brett Chabot3b4fcbc2011-01-09 13:41:02 -080037import android.app.ActivityManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070038import android.app.ActivityManagerNative;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070039import android.app.ActivityThread;
Amith Yamasani3b458ad2013-04-18 18:40:07 -070040import android.app.AppGlobals;
Svetoslavf3f02ac2015-09-08 14:36:35 -070041import android.app.AppOpsManager;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070042import android.app.INotificationManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070043import android.app.Notification;
44import android.app.NotificationManager;
45import android.app.PendingIntent;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000046import android.app.admin.DeviceAdminInfo;
Sander Alewijnseda1350f2014-05-08 16:59:42 +010047import android.app.admin.DevicePolicyManager;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +000048import android.app.admin.DevicePolicyManagerInternal;
Fred Quintanaa698f422009-04-08 19:14:54 -070049import android.content.BroadcastReceiver;
Doug Zongker885cfc232009-10-21 16:52:44 -070050import android.content.ComponentName;
Fred Quintanaa698f422009-04-08 19:14:54 -070051import android.content.ContentValues;
52import android.content.Context;
53import android.content.Intent;
54import android.content.IntentFilter;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070055import android.content.IntentSender;
Fred Quintanab839afc2009-10-14 15:57:28 -070056import android.content.ServiceConnection;
Carlos Valdivia6ede9c32016-03-10 20:12:32 -080057import android.content.pm.ActivityInfo;
Doug Zongker885cfc232009-10-21 16:52:44 -070058import android.content.pm.ApplicationInfo;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070059import android.content.pm.IPackageManager;
Doug Zongker885cfc232009-10-21 16:52:44 -070060import android.content.pm.PackageInfo;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070061import android.content.pm.PackageManager;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -070062import android.content.pm.PackageManager.NameNotFoundException;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070063import android.content.pm.RegisteredServicesCache;
Fred Quintana3ecd5f42009-09-17 12:42:35 -070064import android.content.pm.RegisteredServicesCacheListener;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -070065import android.content.pm.ResolveInfo;
Carlos Valdivia91979be2015-05-22 14:11:35 -070066import android.content.pm.Signature;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070067import android.content.pm.UserInfo;
Fred Quintana60307342009-03-24 22:48:12 -070068import android.database.Cursor;
69import android.database.DatabaseUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070070import android.database.sqlite.SQLiteDatabase;
71import android.database.sqlite.SQLiteOpenHelper;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -070072import android.database.sqlite.SQLiteStatement;
Doug Zongker885cfc232009-10-21 16:52:44 -070073import android.os.Binder;
Fred Quintanaa698f422009-04-08 19:14:54 -070074import android.os.Bundle;
Oscar Montemayora8529f62009-11-18 10:14:20 -080075import android.os.Environment;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070076import android.os.FileUtils;
Fred Quintanaa698f422009-04-08 19:14:54 -070077import android.os.Handler;
Fred Quintanaa698f422009-04-08 19:14:54 -070078import android.os.IBinder;
79import android.os.Looper;
80import android.os.Message;
Dianne Hackborn164371f2013-10-01 19:10:13 -070081import android.os.Parcel;
Amith Yamasani27db4682013-03-30 17:07:47 -070082import android.os.Process;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070083import android.os.RemoteCallback;
Fred Quintanaa698f422009-04-08 19:14:54 -070084import android.os.RemoteException;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -070085import android.os.ServiceManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070086import android.os.SystemClock;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070087import android.os.UserHandle;
Amith Yamasani258848d2012-08-10 17:06:33 -070088import android.os.UserManager;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070089import android.os.storage.StorageManager;
Fred Quintanaa698f422009-04-08 19:14:54 -070090import android.text.TextUtils;
91import android.util.Log;
Svet Ganov5d09c992016-09-07 09:57:41 -070092import android.util.PackageUtils;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -070093import android.util.Pair;
Jeff Sharkey6eb96202012-10-10 13:13:54 -070094import android.util.Slog;
Amith Yamasani04e0d262012-02-14 11:50:53 -080095import android.util.SparseArray;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -070096import android.util.SparseBooleanArray;
Fred Quintana60307342009-03-24 22:48:12 -070097
Costin Manolacheb61e8fb2011-09-08 11:26:09 -070098import com.android.internal.R;
Svet Ganov5d09c992016-09-07 09:57:41 -070099import com.android.internal.annotations.GuardedBy;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700100import com.android.internal.annotations.VisibleForTesting;
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700101import com.android.internal.content.PackageMonitor;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800102import com.android.internal.util.ArrayUtils;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800103import com.android.internal.util.IndentingPrintWriter;
Fyodor Kupolov35f68082016-04-06 12:14:17 -0700104import com.android.internal.util.Preconditions;
Benjamin Franzb6c0ce42015-11-05 10:06:51 +0000105import com.android.server.LocalServices;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700106import com.android.server.ServiceThread;
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600107import com.android.server.SystemService;
108
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700109import com.google.android.collect.Lists;
110import com.google.android.collect.Sets;
Costin Manolacheb61e8fb2011-09-08 11:26:09 -0700111
Oscar Montemayora8529f62009-11-18 10:14:20 -0800112import java.io.File;
Fred Quintanaa698f422009-04-08 19:14:54 -0700113import java.io.FileDescriptor;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700114import java.io.IOException;
Fred Quintanaa698f422009-04-08 19:14:54 -0700115import java.io.PrintWriter;
Sandra Kwan78812282015-11-04 11:19:47 -0800116import java.security.GeneralSecurityException;
Carlos Valdivia91979be2015-05-22 14:11:35 -0700117import java.security.MessageDigest;
118import java.security.NoSuchAlgorithmException;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700119import java.text.SimpleDateFormat;
Fred Quintanaa698f422009-04-08 19:14:54 -0700120import java.util.ArrayList;
Fred Quintana56285a62010-12-02 14:20:51 -0800121import java.util.Arrays;
Fred Quintanaa698f422009-04-08 19:14:54 -0700122import java.util.Collection;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700123import java.util.Date;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700124import java.util.HashMap;
Jeff Sharkey6ab72d72012-10-08 16:44:37 -0700125import java.util.HashSet;
Fred Quintana56285a62010-12-02 14:20:51 -0800126import java.util.LinkedHashMap;
Jeff Sharkey6eb96202012-10-10 13:13:54 -0700127import java.util.List;
Andy McFadden2f362292012-01-20 14:43:38 -0800128import java.util.Map;
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800129import java.util.Map.Entry;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700130import java.util.Set;
Svet Ganovf6d424f12016-09-20 20:18:53 -0700131import java.util.concurrent.CopyOnWriteArrayList;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700132import java.util.concurrent.atomic.AtomicInteger;
133import java.util.concurrent.atomic.AtomicReference;
Fred Quintana60307342009-03-24 22:48:12 -0700134
Fred Quintana60307342009-03-24 22:48:12 -0700135/**
136 * A system service that provides account, password, and authtoken management for all
137 * accounts on the device. Some of these calls are implemented with the help of the corresponding
138 * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
139 * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
Brian Carlstrom46703b02011-04-06 15:41:29 -0700140 * AccountManager accountManager = AccountManager.get(context);
Fred Quintana33269202009-04-20 16:05:10 -0700141 * @hide
Fred Quintana60307342009-03-24 22:48:12 -0700142 */
Fred Quintana3ecd5f42009-09-17 12:42:35 -0700143public class AccountManagerService
144 extends IAccountManager.Stub
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800145 implements RegisteredServicesCacheListener<AuthenticatorDescription> {
Fred Quintana60307342009-03-24 22:48:12 -0700146 private static final String TAG = "AccountManagerService";
147
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600148 public static class Lifecycle extends SystemService {
149 private AccountManagerService mService;
150
151 public Lifecycle(Context context) {
152 super(context);
153 }
154
155 @Override
156 public void onStart() {
157 mService = new AccountManagerService(getContext());
158 publishBinderService(Context.ACCOUNT_SERVICE, mService);
159 }
160
161 @Override
Jeff Sharkey1cab76a2016-04-12 18:23:31 -0600162 public void onUnlockUser(int userHandle) {
163 mService.onUnlockUser(userHandle);
164 }
165 }
166
Fred Quintana60307342009-03-24 22:48:12 -0700167 private static final String DATABASE_NAME = "accounts.db";
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700168 private static final int PRE_N_DATABASE_VERSION = 9;
169 private static final int CE_DATABASE_VERSION = 10;
170 private static final int DE_DATABASE_VERSION = 1;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700171
172 private static final int MAX_DEBUG_DB_SIZE = 64;
Fred Quintana60307342009-03-24 22:48:12 -0700173
Svet Ganov5d09c992016-09-07 09:57:41 -0700174 final Context mContext;
Fred Quintana60307342009-03-24 22:48:12 -0700175
Fred Quintana56285a62010-12-02 14:20:51 -0800176 private final PackageManager mPackageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700177 private final AppOpsManager mAppOpsManager;
Amith Yamasani258848d2012-08-10 17:06:33 -0700178 private UserManager mUserManager;
Fred Quintana56285a62010-12-02 14:20:51 -0800179
Svet Ganov5d09c992016-09-07 09:57:41 -0700180 final MessageHandler mHandler;
Tejas Khorana7b88f0e2016-06-13 13:06:35 -0700181
Fred Quintana60307342009-03-24 22:48:12 -0700182 // Messages that can be sent on mHandler
183 private static final int MESSAGE_TIMED_OUT = 3;
Amith Yamasani5be347b2013-03-31 17:44:31 -0700184 private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
Fred Quintana60307342009-03-24 22:48:12 -0700185
Fred Quintana56285a62010-12-02 14:20:51 -0800186 private final IAccountAuthenticatorCache mAuthenticatorCache;
Fred Quintana60307342009-03-24 22:48:12 -0700187
Svet Ganov5d09c992016-09-07 09:57:41 -0700188 static final String TABLE_ACCOUNTS = "accounts";
189 static final String ACCOUNTS_ID = "_id";
190 static final String ACCOUNTS_NAME = "name";
Fred Quintana60307342009-03-24 22:48:12 -0700191 private static final String ACCOUNTS_TYPE = "type";
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700192 private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
Fred Quintana60307342009-03-24 22:48:12 -0700193 private static final String ACCOUNTS_PASSWORD = "password";
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700194 private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -0800195 private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
196 "last_password_entry_time_millis_epoch";
Fred Quintana60307342009-03-24 22:48:12 -0700197
198 private static final String TABLE_AUTHTOKENS = "authtokens";
199 private static final String AUTHTOKENS_ID = "_id";
200 private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
201 private static final String AUTHTOKENS_TYPE = "type";
202 private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
203
Svet Ganov5d09c992016-09-07 09:57:41 -0700204 static final String TABLE_GRANTS = "grants";
205 static final String GRANTS_ACCOUNTS_ID = "accounts_id";
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700206 private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
Svet Ganov5d09c992016-09-07 09:57:41 -0700207 static final String GRANTS_GRANTEE_UID = "uid";
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700208
Fred Quintana60307342009-03-24 22:48:12 -0700209 private static final String TABLE_EXTRAS = "extras";
210 private static final String EXTRAS_ID = "_id";
211 private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
212 private static final String EXTRAS_KEY = "key";
213 private static final String EXTRAS_VALUE = "value";
214
215 private static final String TABLE_META = "meta";
216 private static final String META_KEY = "key";
217 private static final String META_VALUE = "value";
218
Amith Yamasani67df64b2012-12-14 12:09:36 -0800219 private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700220 private static final String SHARED_ACCOUNTS_ID = "_id";
221
222 private static final String PRE_N_DATABASE_NAME = "accounts.db";
223 private static final String CE_DATABASE_NAME = "accounts_ce.db";
224 private static final String DE_DATABASE_NAME = "accounts_de.db";
225 private static final String CE_DB_PREFIX = "ceDb.";
226 private static final String CE_TABLE_ACCOUNTS = CE_DB_PREFIX + TABLE_ACCOUNTS;
227 private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
228 private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
Amith Yamasani67df64b2012-12-14 12:09:36 -0800229
Jason Parks1cd7d0e2009-09-28 14:48:34 -0700230 private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
231 new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
Fred Quintana7be59642009-08-24 18:29:25 -0700232 private static final Intent ACCOUNTS_CHANGED_INTENT;
Sandra Kwan390c9d22016-01-12 14:13:37 -0800233
Carlos Valdivia91979be2015-05-22 14:11:35 -0700234 static {
235 ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
236 ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
237 }
Fred Quintanaa698f422009-04-08 19:14:54 -0700238
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700239 private static final String COUNT_OF_MATCHING_GRANTS = ""
240 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
241 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
242 + " AND " + GRANTS_GRANTEE_UID + "=?"
243 + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
244 + " AND " + ACCOUNTS_NAME + "=?"
245 + " AND " + ACCOUNTS_TYPE + "=?";
246
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700247 private static final String COUNT_OF_MATCHING_GRANTS_ANY_TOKEN = ""
248 + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
249 + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
250 + " AND " + GRANTS_GRANTEE_UID + "=?"
251 + " AND " + ACCOUNTS_NAME + "=?"
252 + " AND " + ACCOUNTS_TYPE + "=?";
253
Fred Quintana56285a62010-12-02 14:20:51 -0800254 private static final String SELECTION_AUTHTOKENS_BY_ACCOUNT =
255 AUTHTOKENS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
Carlos Valdivia91979be2015-05-22 14:11:35 -0700256
Fred Quintana56285a62010-12-02 14:20:51 -0800257 private static final String[] COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN = {AUTHTOKENS_TYPE,
258 AUTHTOKENS_AUTHTOKEN};
259
260 private static final String SELECTION_USERDATA_BY_ACCOUNT =
261 EXTRAS_ACCOUNTS_ID + "=(select _id FROM accounts WHERE name=? AND type=?)";
262 private static final String[] COLUMNS_EXTRAS_KEY_AND_VALUE = {EXTRAS_KEY, EXTRAS_VALUE};
263
Sandra Kwan1c9026d2016-02-23 10:22:15 -0800264 private static final String META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX =
265 "auth_uid_for_type:";
266 private static final String META_KEY_DELIMITER = ":";
267 private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
268
Fred Quintanaa698f422009-04-08 19:14:54 -0700269 private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700270 private final AtomicInteger mNotificationIds = new AtomicInteger(1);
271
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700272 private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
273
Amith Yamasani04e0d262012-02-14 11:50:53 -0800274 static class UserAccounts {
275 private final int userId;
Svet Ganov5d09c992016-09-07 09:57:41 -0700276 final DeDatabaseHelper openHelper;
Amith Yamasani04e0d262012-02-14 11:50:53 -0800277 private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
278 credentialsPermissionNotificationIds =
279 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
280 private final HashMap<Account, Integer> signinRequiredNotificationIds =
281 new HashMap<Account, Integer>();
Svet Ganov5d09c992016-09-07 09:57:41 -0700282 final Object cacheLock = new Object();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800283 /** protected by the {@link #cacheLock} */
Svet Ganov5d09c992016-09-07 09:57:41 -0700284 final HashMap<String, Account[]> accountCache =
Svet Ganovf6d424f12016-09-20 20:18:53 -0700285 new LinkedHashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800286 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700287 private final Map<Account, Map<String, String>> userDataCache = new HashMap<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800288 /** protected by the {@link #cacheLock} */
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -0700289 private final Map<Account, Map<String, String>> authTokenCache = new HashMap<>();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700290
291 /** protected by the {@link #cacheLock} */
Carlos Valdiviac37ee222015-06-17 20:17:37 -0700292 private final TokenCache accountTokenCaches = new TokenCache();
Carlos Valdivia91979be2015-05-22 14:11:35 -0700293
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700294 /** protected by the {@link #cacheLock} */
295 private final Map<String, ArrayList<Integer>> mApplicationAccountRequestMappings =
296 new HashMap<>();
297
298 /* Together the below two Sparse Arrays serve as visible list. One maps UID to account
299 number. Another maps Account number to Account.*/
300
301 /** protected by the {@link #cacheLock} */
302 private final SparseArray<ArrayList<Integer>> mVisibleListUidToMockAccountNumbers =
303 new SparseArray<>();
304
305 //TODO: Instead of using Mock Account IDs, use the actual account IDs.
306 /** protected by the {@link #cacheLock} */
307 private final SparseArray<Account> mMockAccountIdToAccount = new SparseArray<>();
308
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -0700309 /**
310 * protected by the {@link #cacheLock}
311 *
312 * Caches the previous names associated with an account. Previous names
313 * should be cached because we expect that when an Account is renamed,
314 * many clients will receive a LOGIN_ACCOUNTS_CHANGED broadcast and
315 * want to know if the accounts they care about have been renamed.
316 *
317 * The previous names are wrapped in an {@link AtomicReference} so that
318 * we can distinguish between those accounts with no previous names and
319 * those whose previous names haven't been cached (yet).
320 */
321 private final HashMap<Account, AtomicReference<String>> previousNameCache =
322 new HashMap<Account, AtomicReference<String>>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800323
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -0700324 private int debugDbInsertionPoint = -1;
325 private SQLiteStatement statementForLogging;
326
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700327 UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
Amith Yamasani04e0d262012-02-14 11:50:53 -0800328 this.userId = userId;
329 synchronized (cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -0700330 openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
Amith Yamasani04e0d262012-02-14 11:50:53 -0800331 }
332 }
333 }
334
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700335 private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
Jeff Sharkeyce18c812016-04-27 16:00:41 -0600336 private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
Svet Ganovf6d424f12016-09-20 20:18:53 -0700337 private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
338 mAppPermissionChangeListeners = new CopyOnWriteArrayList<>();
Amith Yamasani04e0d262012-02-14 11:50:53 -0800339
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -0700340 private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
Fred Quintana31957f12009-10-21 13:43:10 -0700341 private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
Fred Quintana7be59642009-08-24 18:29:25 -0700342
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700343 /**
344 * This should only be called by system code. One should only call this after the service
345 * has started.
346 * @return a reference to the AccountManagerService instance
347 * @hide
348 */
349 public static AccountManagerService getSingleton() {
350 return sThis.get();
351 }
Fred Quintana60307342009-03-24 22:48:12 -0700352
Fred Quintana56285a62010-12-02 14:20:51 -0800353 public AccountManagerService(Context context) {
354 this(context, context.getPackageManager(), new AccountAuthenticatorCache(context));
Fred Quintana60307342009-03-24 22:48:12 -0700355 }
356
Fred Quintana56285a62010-12-02 14:20:51 -0800357 public AccountManagerService(Context context, PackageManager packageManager,
358 IAccountAuthenticatorCache authenticatorCache) {
Fred Quintana60307342009-03-24 22:48:12 -0700359 mContext = context;
Fred Quintana56285a62010-12-02 14:20:51 -0800360 mPackageManager = packageManager;
Svetoslavf3f02ac2015-09-08 14:36:35 -0700361 mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
Fred Quintana60307342009-03-24 22:48:12 -0700362
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700363 ServiceThread serviceThread = new ServiceThread(TAG,
364 android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
365 serviceThread.start();
366 mHandler = new MessageHandler(serviceThread.getLooper());
Fred Quintana60307342009-03-24 22:48:12 -0700367
Fred Quintana56285a62010-12-02 14:20:51 -0800368 mAuthenticatorCache = authenticatorCache;
Fred Quintana5ebbb4a2009-11-09 15:42:20 -0800369 mAuthenticatorCache.setListener(this, null /* Handler */);
Fred Quintana60307342009-03-24 22:48:12 -0700370
Fred Quintanad4a1d2e2009-07-16 16:36:38 -0700371 sThis.set(this);
Fred Quintanaafa92b82009-12-01 16:27:03 -0800372
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700373 addRequestsForPreInstalledApplications();
374
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800375 IntentFilter intentFilter = new IntentFilter();
376 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
377 intentFilter.addDataScheme("package");
378 mContext.registerReceiver(new BroadcastReceiver() {
379 @Override
380 public void onReceive(Context context1, Intent intent) {
Carlos Valdivia23f58262014-09-05 10:52:41 -0700381 // Don't delete accounts when updating a authenticator's
382 // package.
383 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700384 /* Purging data requires file io, don't block the main thread. This is probably
385 * less than ideal because we are introducing a race condition where old grants
386 * could be exercised until they are purged. But that race condition existed
387 * anyway with the broadcast receiver.
388 *
389 * Ideally, we would completely clear the cache, purge data from the database,
390 * and then rebuild the cache. All under the cache lock. But that change is too
391 * large at this point.
392 */
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700393 Runnable purgingRunnable = new Runnable() {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700394 @Override
395 public void run() {
396 purgeOldGrantsAll();
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700397
398 /* clears application request's for account types supported */
399 int uidOfUninstalledApplication =
400 intent.getIntExtra(Intent.EXTRA_UID, -1);
401 if(uidOfUninstalledApplication != -1) {
402 clearRequestedAccountVisibility(uidOfUninstalledApplication,
403 getUserAccounts(UserHandle.getUserId(
404 uidOfUninstalledApplication)));
405 }
406
407 /* removes visibility of previous UID of this uninstalled application*/
408 removeAccountVisibilityAllAccounts(uidOfUninstalledApplication,
409 getUserAccounts(UserHandle.getUserId(
410 uidOfUninstalledApplication)));
Carlos Valdiviaa3721e12015-08-10 18:40:06 -0700411 }
412 };
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700413 mHandler.post(purgingRunnable);
Carlos Valdivia23f58262014-09-05 10:52:41 -0700414 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700415
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800416 }
417 }, intentFilter);
Fred Quintanac1a4e5d2011-02-25 10:44:38 -0800418
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700419 IntentFilter packageAddedOrChangedFilter = new IntentFilter();
420 intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
421 packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
422 packageAddedOrChangedFilter.addDataScheme("package");
423 mContext.registerReceiverAsUser(new BroadcastReceiver() {
424 @Override
425 public void onReceive(Context context1, Intent intent) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700426 mHandler.post(new Runnable() {
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700427 @Override
428 public void run() {
429 int uidOfInstalledApplication =
430 intent.getIntExtra(Intent.EXTRA_UID, -1);
431 if(uidOfInstalledApplication != -1) {
432 registerAccountTypesSupported(
433 uidOfInstalledApplication,
434 getUserAccounts(
435 UserHandle.getUserId(uidOfInstalledApplication)));
436 }
437 }
438 });
439 }
440 }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
441
Amith Yamasani13593602012-03-22 16:16:17 -0700442 IntentFilter userFilter = new IntentFilter();
443 userFilter.addAction(Intent.ACTION_USER_REMOVED);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800444 mContext.registerReceiverAsUser(new BroadcastReceiver() {
Amith Yamasani13593602012-03-22 16:16:17 -0700445 @Override
446 public void onReceive(Context context, Intent intent) {
Amith Yamasani67df64b2012-12-14 12:09:36 -0800447 String action = intent.getAction();
448 if (Intent.ACTION_USER_REMOVED.equals(action)) {
449 onUserRemoved(intent);
Amith Yamasani67df64b2012-12-14 12:09:36 -0800450 }
Amith Yamasani13593602012-03-22 16:16:17 -0700451 }
Amith Yamasani67df64b2012-12-14 12:09:36 -0800452 }, UserHandle.ALL, userFilter, null, null);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700453
454 LocalServices.addService(AccountManagerInternal.class, new AccountManagerInternalImpl());
455
456 // Need to cancel account request notifications if the update/install can access the account
457 new PackageMonitor() {
458 @Override
459 public void onPackageAdded(String packageName, int uid) {
460 // Called on a handler, and running as the system
461 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
462 }
463
464 @Override
465 public void onPackageUpdateFinished(String packageName, int uid) {
466 // Called on a handler, and running as the system
467 cancelAccountAccessRequestNotificationIfNeeded(uid, true);
468 }
Fyodor Kupolov8873aa32016-08-25 15:25:40 -0700469 }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700470
471 // Cancel account request notification if an app op was preventing the account access
472 mAppOpsManager.startWatchingMode(AppOpsManager.OP_GET_ACCOUNTS, null,
473 new AppOpsManager.OnOpChangedInternalListener() {
474 @Override
475 public void onOpChanged(int op, String packageName) {
476 try {
477 final int userId = ActivityManager.getCurrentUser();
478 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
479 final int mode = mAppOpsManager.checkOpNoThrow(
480 AppOpsManager.OP_GET_ACCOUNTS, uid, packageName);
481 if (mode == AppOpsManager.MODE_ALLOWED) {
482 final long identity = Binder.clearCallingIdentity();
483 try {
484 cancelAccountAccessRequestNotificationIfNeeded(packageName, uid, true);
485 } finally {
486 Binder.restoreCallingIdentity(identity);
487 }
488 }
489 } catch (NameNotFoundException e) {
490 /* ignore */
491 }
492 }
493 });
494
495 // Cancel account request notification if a permission was preventing the account access
496 mPackageManager.addOnPermissionsChangeListener(
497 (int uid) -> {
498 Account[] accounts = null;
499 String[] packageNames = mPackageManager.getPackagesForUid(uid);
500 if (packageNames != null) {
501 final int userId = UserHandle.getUserId(uid);
502 final long identity = Binder.clearCallingIdentity();
503 try {
504 for (String packageName : packageNames) {
505 if (mContext.getPackageManager().checkPermission(
506 Manifest.permission.GET_ACCOUNTS, packageName)
507 != PackageManager.PERMISSION_GRANTED) {
508 continue;
509 }
510
511 if (accounts == null) {
512 accounts = getAccountsAsUser(null, userId, "android");
513 if (ArrayUtils.isEmpty(accounts)) {
514 return;
515 }
516 }
517
518 for (Account account : accounts) {
519 cancelAccountAccessRequestNotificationIfNeeded(
520 account, uid, packageName, true);
521 }
522 }
523 } finally {
524 Binder.restoreCallingIdentity(identity);
525 }
526 }
527 });
528 }
529
530 private void cancelAccountAccessRequestNotificationIfNeeded(int uid,
531 boolean checkAccess) {
532 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
533 for (Account account : accounts) {
534 cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess);
535 }
536 }
537
538 private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid,
539 boolean checkAccess) {
540 Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android");
541 for (Account account : accounts) {
542 cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess);
543 }
544 }
545
546 private void cancelAccountAccessRequestNotificationIfNeeded(Account account, int uid,
547 boolean checkAccess) {
548 String[] packageNames = mPackageManager.getPackagesForUid(uid);
549 if (packageNames != null) {
550 for (String packageName : packageNames) {
551 cancelAccountAccessRequestNotificationIfNeeded(account, uid,
552 packageName, checkAccess);
553 }
554 }
555 }
556
557 private void cancelAccountAccessRequestNotificationIfNeeded(Account account,
558 int uid, String packageName, boolean checkAccess) {
559 if (!checkAccess || hasAccountAccess(account, packageName,
560 UserHandle.getUserHandleForUid(uid))) {
561 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -0700562 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -0700563 UserHandle.getUserHandleForUid(uid));
564 }
Fred Quintanaafa92b82009-12-01 16:27:03 -0800565 }
566
Dianne Hackborn164371f2013-10-01 19:10:13 -0700567 @Override
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700568 public boolean addAccountExplicitlyWithUid(Account account, String password, Bundle extras,
569 int[] selectedUids) {
570 if(addAccountExplicitly(account,password,extras)) {
571 for(int thisUid : selectedUids) {
572 makeAccountVisible(account, thisUid);
573 }
574 return true;
575 }
576 return false;
577 }
578
579 @Override
580 public int[] getRequestingUidsForType(String accountType) {
581 int callingUid = Binder.getCallingUid();
582 if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
583 String msg = String.format(
584 "uid %s cannot get secrets for accounts of type: %s",
585 callingUid,
586 accountType);
587 throw new SecurityException(msg);
588 }
589 return getRequestingUidsForType(accountType, getUserAccounts(
590 UserHandle.getUserId(callingUid)));
591 }
592
593 /**
594 * Returns all UIDs for applications that requested the account type. This method
595 * is called indirectly by the Authenticator and AccountManager
596 *
597 * @param accountType authenticator would like to know the requesting apps of
598 * @param ua UserAccount that currently hosts the account and application
599 *
600 * @return ArrayList of all UIDs that support accounts of this
601 * account type that seek approval (to be used to know which accounts for
602 * the authenticator to include in addAccountExplicitly). Null if none.
603 */
604 private int[] getRequestingUidsForType(String accountType, UserAccounts ua) {
605 synchronized(ua.cacheLock) {
606 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
607 ua.mApplicationAccountRequestMappings;
608 ArrayList<Integer> allUidsForAccountType = userApplicationAccountRequestMappings.get(
609 accountType);
610 if(allUidsForAccountType == null) {
611 return null;
612 }
613 int[] toReturn = new int[allUidsForAccountType.size()];
614 for(int i = 0 ; i < toReturn.length ; i++) {
615 toReturn[i] = allUidsForAccountType.get(i);
616 }
617 return toReturn;
618 }
619 }
620
621 @Override
622 public boolean isAccountVisible(Account a, int uid) {
623 int callingUid = Binder.getCallingUid();
624 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
625 String msg = String.format(
626 "uid %s cannot get secrets for accounts of type: %s",
627 callingUid,
628 a.type);
629 throw new SecurityException(msg);
630 }
631 return isAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
632 }
633
634 /**
635 * Checks visibility of certain account of a process identified
636 * by a given UID. This is called by the Authenticator indirectly.
637 *
638 * @param a The account to check visibility of
639 * @param uid UID to check visibility of
640 * @param ua UserAccount that currently hosts the account and application
641 *
642 * @return True if application has access to the account
643 *
644 */
645 private boolean isAccountVisible(Account a, int uid, UserAccounts ua) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700646 if(isAccountManagedByCaller(a.type, uid, UserHandle.getUserId(uid))) {
647 return true;
648 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700649 int accountMapping = getMockAccountNumber(a, ua);
650 if(accountMapping < 0) {
651 return true;
652 }
653 synchronized(ua.cacheLock) {
Tejas Khorana69990d92016-08-03 11:19:40 -0700654 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700655 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
656 ua.mVisibleListUidToMockAccountNumbers;
657 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
Tejas Khorana69990d92016-08-03 11:19:40 -0700658 int indexOfAccountMapping = userAcctIdToAcctMap.indexOfValueByValue(a);
659 return indexOfAccountMapping == -1 || (linkedAccountsToUid != null
660 && linkedAccountsToUid.contains(accountMapping));
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700661 }
662 }
663
664 @Override
665 public boolean makeAccountVisible(Account a, int uid) {
666 int callingUid = Binder.getCallingUid();
667 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
668 String msg = String.format(
669 "uid %s cannot get secrets for accounts of type: %s",
670 callingUid,
671 a.type);
672 throw new SecurityException(msg);
673 }
674 return makeAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
675 }
676
677 /**
678 * Gives a certain UID, represented a application, access to an account. This method
679 * is called indirectly by the Authenticator.
680 *
681 * @param a Account to make visible
682 * @param uid to add visibility of the Account from
683 * @param ua UserAccount that currently hosts the account and application
684 *
685 * @return True if account made visible to application and was not previously visible.
686 */
687 private boolean makeAccountVisible(Account a, int uid, UserAccounts ua) {
688 int accountMapping = getMockAccountNumber(a, ua);
689 if(accountMapping < 0) {
690 accountMapping = makeAccountNumber(a, ua);
691 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700692 synchronized(ua.cacheLock) {
693 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
694 ua.mVisibleListUidToMockAccountNumbers;
695 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
696 if(linkedAccountsToUid == null) {
697 linkedAccountsToUid = new ArrayList<>();
698 linkedAccountsToUid.add(accountMapping);
699 userWlUidToMockAccountNums.put(uid, linkedAccountsToUid);
700 } else if(!linkedAccountsToUid.contains(accountMapping)) {
701 linkedAccountsToUid.add(accountMapping);
702 } else {
703 return false;
704 }
705 }
706
707 String[] subPackages = mPackageManager.getPackagesForUid(uid);
708 if(subPackages != null) {
709 for(String subPackage : subPackages) {
710 sendNotification(subPackage, a);
711 }
712 }
713 return true;
714 }
715
716 @Override
717 public boolean removeAccountVisibility(Account a, int uid) {
718 int callingUid = Binder.getCallingUid();
719 if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) {
720 String msg = String.format(
721 "uid %s cannot get secrets for accounts of type: %s",
722 callingUid,
723 a.type);
724 throw new SecurityException(msg);
725 }
726 return removeAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
727 }
728
729 /**
730 * Removes visibility of certain account of a process identified
731 * by a given UID to an application. This is called directly by the
732 * AccountManager and indirectly by the Authenticator.
733 *
734 * @param a Account to remove visibility from
735 * @param uid UID to remove visibility of the Account from
736 * @param ua UserAccount that hosts the account and application
737 *
738 * @return True if application access to account removed and was previously visible.
739 */
740 private boolean removeAccountVisibility(Account a, int uid, UserAccounts ua) {
741 int accountMapping = getMockAccountNumber(a, ua);
742 if(accountMapping < 0) {
743 return false;
744 }
745 synchronized(ua.cacheLock) {
746 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
747 ua.mVisibleListUidToMockAccountNumbers;
748 ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid);
749 if(linkedAccountsToUid != null) {
750 boolean toReturn = linkedAccountsToUid.remove((Integer) accountMapping);
751 if(linkedAccountsToUid.size() == 0) {
752 userWlUidToMockAccountNums.remove(uid);
753 }
754 return toReturn;
755 }
756 }
757 return false;
758 }
759
760 /**
761 * Registers an application's preferences for supported account types for login. This is
762 * a helper method of requestAccountVisibility and indirectly called by AccountManager.
763 *
764 * @param accountTypes account types third party app is willing to support
765 * @param uid of application requesting account visibility
766 * @param ua UserAccount that hosts the account and application
767 */
768 private void addRequestedAccountsVisibility(String[] accountTypes, int uid, UserAccounts ua) {
769 synchronized(ua.cacheLock) {
770 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
771 ua.mApplicationAccountRequestMappings;
772 for(String accountType : accountTypes) {
773 ArrayList<Integer> accountUidAppList = userApplicationAccountRequestMappings
774 .get(accountType);
775 if(accountUidAppList == null) {
776 accountUidAppList = new ArrayList<>();
777 accountUidAppList.add(uid);
778 userApplicationAccountRequestMappings.put(accountType, accountUidAppList);
779 } else if (!accountUidAppList.contains(uid)) {
780 accountUidAppList.add(uid);
781 }
782 }
783 }
784 }
785
786 /**
787 * Registers the requested login account types requested by all the applications already
788 * installed on the device.
789 */
790 private void addRequestsForPreInstalledApplications() {
791 List<PackageInfo> allInstalledPackages = mContext.getPackageManager().
792 getInstalledPackages(0);
793 for(PackageInfo pi : allInstalledPackages) {
794 int currentUid = pi.applicationInfo.uid;
795 if(currentUid != -1) {
796 registerAccountTypesSupported(currentUid,
797 getUserAccounts(UserHandle.getUserId(currentUid)));
798 }
799 }
800 }
801
802 /**
803 * Clears all preferences an application had for login account types it offered
804 * support for. This method is used by AccountManager after application is
805 * uninstalled.
806 *
807 * @param uid Uid of the application to clear account type preferences
808 * @param ua UserAccount that hosted the account and application
809 *
810 * @return true if any previous settings were overridden.
811 */
812 private boolean clearRequestedAccountVisibility(int uid, UserAccounts ua) {
813 boolean accountsDeleted = false;
814 ArrayList<String> accountTypesToRemove = new ArrayList<>();
815 synchronized(ua.cacheLock) {
816 Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings =
817 ua.mApplicationAccountRequestMappings;
818 Set<Entry<String, ArrayList<Integer>>> accountTypeAppListEntries =
819 userApplicationAccountRequestMappings.entrySet();
820
821 for(Entry<String, ArrayList<Integer>> entry : accountTypeAppListEntries) {
822 ArrayList<Integer> supportedApps = entry.getValue();
823 if(supportedApps.remove((Integer) uid)) {
824 accountsDeleted = true;
825 }
826
827 if(supportedApps.isEmpty()) {
828 accountTypesToRemove.add(entry.getKey());
829 }
830 }
831
832 for(String s : accountTypesToRemove) {
833 userApplicationAccountRequestMappings.remove(s);
834 }
835 }
836
837 return accountsDeleted;
838 }
839
840 /**
841 * Retrieves the mock account number associated with an Account in order to later retrieve
842 * the account from the Integer-Account Mapping. An account number is not the same as
843 * accountId in the database. This method can be indirectly called by AccountManager and
844 * indirectly by the Authenticator.
845 *
846 * @param a account to retrieve account number mapping
847 * @param ua UserAccount that currently hosts the account and application
848 *
849 * @return account number affiliated with the Account in question. Negative number if none.
850 */
851 private int getMockAccountNumber(Account a, UserAccounts ua) {
852 //TODO: Each account is linked to AccountId rather than generated mock account numbers
853 SparseArray<Account> userAcctIdToAcctMap =
854 ua.mMockAccountIdToAccount;
855 synchronized(ua.cacheLock) {
856 int indexOfAccount = userAcctIdToAcctMap.indexOfValueByValue(a);
857 if(indexOfAccount < 0) {
858 return -1;
859 }
860 return userAcctIdToAcctMap.keyAt(indexOfAccount);
861 }
862 }
863
864 /**
865 * Returns a full list of accounts that a certain UID is allowed access
866 * based on the visible list entries.
867 *
868 * @param uid of application to retrieve visible listed accounts for
869 * @param ua UserAccount that currently hosts the account and application
870 *
871 * @return array of Account values that are accessible by the given uids
872 */
873 private Account[] getVisibleListedAccounts(int uid, UserAccounts ua) {
874 ArrayList<Account> visibleListedAccounts = new ArrayList<>();
875 synchronized(ua.cacheLock) {
876 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
877 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
878 ua.mVisibleListUidToMockAccountNumbers;
879 ArrayList<Integer> visibleListedUidAccountNumbers =
880 userWlUidToMockAccountNums.get(uid);
881 if(visibleListedUidAccountNumbers != null) {
882 for(Integer accountNumber : visibleListedUidAccountNumbers) {
883 Account currentAccount = userAcctIdToAcctMap.get(accountNumber);
884 visibleListedAccounts.add(currentAccount);
885 }
886 }
887 }
888 Account[] arrVisibleListedAccounts = new Account[visibleListedAccounts.size()];
889 return visibleListedAccounts.toArray(arrVisibleListedAccounts);
890 }
891
892 /**
893 * Makes an account number for a given Account to be mapped to.
894 * This method is called by makeVisible if an Account does not have
895 * a mapping for the visible list. This method is thus indirectly
896 * called by the Authenticator.
897 *
898 * @param a account to make an account number mapping of
899 * @param ua UserAccount that currently hosts the account and application
900 *
901 * @return account number created to map to the given account
902 */
903 // TODO: Remove this method and use accountId from DB.
904 private int makeAccountNumber(Account a, UserAccounts ua) {
905 synchronized(ua.cacheLock) {
906 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
907 int newAccountMapping = 0;
908 while(userAcctIdToAcctMap.get(newAccountMapping) != null) {
909 newAccountMapping++;
910 }
911 userAcctIdToAcctMap.put(newAccountMapping, a);
912 return newAccountMapping;
913 }
914 }
915
916
917
918 /**
919 * Registers an application, represented by a UID, to support account types detailed in
920 * the applications manifest as well as allowing it to opt for notifications.
921 *
922 * @param uid UID of application
923 * @param ua UserAccount that currently hosts the account and application
924 */
925 private void registerAccountTypesSupported(int uid, UserAccounts ua) {
926 /* Account types supported are drawn from the Android Manifest of the Application */
927 String interestedPackages = null;
928 try {
929 String[] allPackages = mPackageManager.getPackagesForUid(uid);
Nicolas Prevotf7d8df12016-09-16 17:45:34 +0100930 if (allPackages != null) {
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");
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700939 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -0700940 }
941 } catch (PackageManager.NameNotFoundException e) {
942 Log.d("NameNotFoundException", e.getMessage());
943 }
944 if(interestedPackages != null) {
945 /* request remote account types directly from here. Reads from Android Manifest */
946 requestAccountVisibility(interestedPackages.split(";"), uid, ua);
947 }
948 }
949
950 /**
951 * Allows AccountManager to register account types that an application has login
952 * support for. This method over-writes all of the application's previous settings
953 * for accounts it supported.
954 *
955 * @param accountTypes array of account types application wishes to support
956 * @param uid of application registering requested account types
957 * @param ua UserAccount that hosts the account and application
958 */
959 private void requestAccountVisibility(String[] accountTypes, int uid, UserAccounts ua) {
960 if(accountTypes.length > 0) {
961 clearRequestedAccountVisibility(uid, ua);
962 addRequestedAccountsVisibility(accountTypes, uid, ua);
963 }
964 }
965
966 /**
967 * Removes visibility of all Accounts to this particular UID. This is called when an
968 * application is uninstalled so another application that is installed with the same
969 * UID cannot access Accounts. This is called by AccountManager.
970 *
971 * @param uid of application to remove all Account visibility to
972 * @param ua UserAccount that hosts the current Account
973 */
974 private void removeAccountVisibilityAllAccounts(int uid, UserAccounts ua) {
975 synchronized(ua.cacheLock) {
976 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
977 ua.mVisibleListUidToMockAccountNumbers;
978 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
979 ArrayList<Integer> allAccountNumbersList = userWlUidToMockAccountNums.get(uid);
980 if(allAccountNumbersList != null) {
981 Integer[] allAccountNumbers = allAccountNumbersList.toArray(
982 new Integer[allAccountNumbersList.size()]);
983 for(int accountNum : allAccountNumbers) {
984 removeAccountVisibility(userAcctIdToAcctMap.get(accountNum), uid, ua);
985 }
986 }
987 }
988 }
989
990 /**
991 * Removes visible list functionality of a certain Account.
992 * This method is currently called by (1) addAccountExplicitly (as opposed to
993 * addAccountExplicitlyWithUid) and (2) removeAccountExplicitly.
994 *
995 * @param a the account to clear the visible list functionality for
996 * @param ua currently UserAccounts profile containing Account
997 *
998 * @return true if account previously had visible list functionality
999 */
1000 private boolean removeVisibleListFunctionality(Account a, UserAccounts ua) {
1001 int mockAccountNum = getMockAccountNumber(a, ua);
1002 if(mockAccountNum < 0) {
1003 return false;
1004 }
1005 synchronized(ua.cacheLock) {
1006 SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums =
1007 ua.mVisibleListUidToMockAccountNumbers;
1008 SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount;
1009
1010 /* Removing mapping from account number to account removes visible list functionality*/
1011 userAcctIdToAcctMap.remove(mockAccountNum);
1012
1013 for(int i = userWlUidToMockAccountNums.size() - 1 ; i >= 0 ; i--) {
1014 int uidKey = userWlUidToMockAccountNums.keyAt(i);
1015 ArrayList<Integer> allAccountNumbers = userWlUidToMockAccountNums.get(uidKey);
1016 if(allAccountNumbers != null) {
1017 allAccountNumbers.remove(mockAccountNum);
1018 if(allAccountNumbers.isEmpty()) {
1019 userWlUidToMockAccountNums.remove(uidKey);
1020 }
1021 }
1022 }
1023 }
1024 return true;
1025 }
1026
1027 /**
1028 * Sends a direct intent to a package, notifying it of a visible account. This
1029 * method is a helper method of makeAccountVisible.
1030 *
1031 * @param desiredPackage to send Account to
1032 * @param visibleAccount to send to package
1033 */
1034 private void sendNotification(String desiredPackage, Account visibleAccount) {
1035 Intent intent = new Intent();
1036 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
1037 intent.setAction(NEW_ACCOUNT_VISIBLE);
1038 intent.setPackage(desiredPackage);
1039 intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
1040 mContext.sendBroadcast(intent);
1041 }
1042
1043 @Override
Dianne Hackborn164371f2013-10-01 19:10:13 -07001044 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
1045 throws RemoteException {
1046 try {
1047 return super.onTransact(code, data, reply, flags);
1048 } catch (RuntimeException e) {
1049 // The account manager only throws security exceptions, so let's
1050 // log all others.
1051 if (!(e instanceof SecurityException)) {
1052 Slog.wtf(TAG, "Account Manager Crash", e);
1053 }
1054 throw e;
1055 }
1056 }
1057
Amith Yamasani258848d2012-08-10 17:06:33 -07001058 private UserManager getUserManager() {
1059 if (mUserManager == null) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001060 mUserManager = UserManager.get(mContext);
Amith Yamasani258848d2012-08-10 17:06:33 -07001061 }
1062 return mUserManager;
1063 }
1064
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001065 /**
1066 * Validate internal set of accounts against installed authenticators for
1067 * given user. Clears cached authenticators before validating.
1068 */
1069 public void validateAccounts(int userId) {
1070 final UserAccounts accounts = getUserAccounts(userId);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001071 // Invalidate user-specific cache to make sure we catch any
1072 // removed authenticators.
1073 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
1074 }
1075
1076 /**
1077 * Validate internal set of accounts against installed authenticators for
1078 * given user. Clear cached authenticators before validating when requested.
1079 */
1080 private void validateAccountsInternal(
1081 UserAccounts accounts, boolean invalidateAuthenticatorCache) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001082 if (Log.isLoggable(TAG, Log.DEBUG)) {
1083 Log.d(TAG, "validateAccountsInternal " + accounts.userId
1084 + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001085 + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001086 }
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001087
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001088 if (invalidateAuthenticatorCache) {
1089 mAuthenticatorCache.invalidateCache(accounts.userId);
1090 }
1091
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001092 final HashMap<String, Integer> knownAuth = getAuthenticatorTypeAndUIDForUser(
1093 mAuthenticatorCache, accounts.userId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001094 boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001095
Amith Yamasani04e0d262012-02-14 11:50:53 -08001096 synchronized (accounts.cacheLock) {
1097 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001098 boolean accountDeleted = false;
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001099
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001100 // Get a map of stored authenticator types to UID
1101 Map<String, Integer> metaAuthUid = AccountsDbUtils.findMetaAuthUid(db);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001102 // Create a list of authenticator type whose previous uid no longer exists
1103 HashSet<String> obsoleteAuthType = Sets.newHashSet();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001104 SparseBooleanArray knownUids = null;
1105 for (Entry<String, Integer> authToUidEntry : metaAuthUid.entrySet()) {
1106 String type = authToUidEntry.getKey();
1107 int uid = authToUidEntry.getValue();
1108 Integer knownUid = knownAuth.get(type);
1109 if (knownUid != null && uid == knownUid) {
1110 // Remove it from the knownAuth list if it's unchanged.
1111 knownAuth.remove(type);
1112 } else {
1113 /*
1114 * The authenticator is presently not cached and should only be triggered
1115 * when we think an authenticator has been removed (or is being updated).
1116 * But we still want to check if any data with the associated uid is
1117 * around. This is an (imperfect) signal that the package may be updating.
1118 *
1119 * A side effect of this is that an authenticator sharing a uid with
1120 * multiple apps won't get its credentials wiped as long as some app with
1121 * that uid is still on the device. But I suspect that this is a rare case.
1122 * And it isn't clear to me how an attacker could really exploit that
1123 * feature.
1124 *
1125 * The upshot is that we don't have to worry about accounts getting
1126 * uninstalled while the authenticator's package is being updated.
1127 *
1128 */
1129 if (knownUids == null) {
1130 knownUids = getUidsOfInstalledOrUpdatedPackagesAsUser(accounts.userId);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001131 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001132 if (!knownUids.get(uid)) {
1133 // The authenticator is not presently available to the cache. And the
1134 // package no longer has a data directory (so we surmise it isn't updating).
1135 // So purge its data from the account databases.
1136 obsoleteAuthType.add(type);
1137 // And delete it from the TABLE_META
1138 AccountsDbUtils.deleteMetaByAuthTypeAndUid(db, type, uid);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001139 }
1140 }
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001141 }
1142
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001143 // Add the newly registered authenticator to TABLE_META. If old authenticators have
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001144 // been re-enabled (after being updated for example), then we just overwrite the old
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001145 // values.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001146 for (Entry<String, Integer> entry : knownAuth.entrySet()) {
1147 AccountsDbUtils.insertOrReplaceMetaAuthTypeAndUid(db, entry.getKey(),
1148 entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001149 }
1150
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001151 final Map<Long, Account> accountsMap = AccountsDbUtils.findAllAccounts(db);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001152 try {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001153 accounts.accountCache.clear();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001154 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001155 for (Entry<Long, Account> accountEntry : accountsMap.entrySet()) {
1156 final long accountId = accountEntry.getKey();
1157 final Account account = accountEntry.getValue();
1158 if (obsoleteAuthType.contains(account.type)) {
1159 Slog.w(TAG, "deleting account " + account.name + " because type "
1160 + account.type + "'s registered authenticator no longer exist.");
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001161 db.beginTransaction();
1162 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001163 AccountsDbUtils.deleteAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001164 // Also delete from CE table if user is unlocked; if user is currently
1165 // locked the account will be removed later by syncDeCeAccountsLocked
1166 if (userUnlocked) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001167 AccountsDbUtils.deleteCeAccount(db, accountId);
Fyodor Kupolov627fc202016-06-03 11:03:03 -07001168 }
1169 db.setTransactionSuccessful();
1170 } finally {
1171 db.endTransaction();
1172 }
Fred Quintana56285a62010-12-02 14:20:51 -08001173 accountDeleted = true;
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001174
1175 logRecord(db, DebugDbHelper.ACTION_AUTHENTICATOR_REMOVE, TABLE_ACCOUNTS,
1176 accountId, accounts);
1177
Amith Yamasani04e0d262012-02-14 11:50:53 -08001178 accounts.userDataCache.remove(account);
1179 accounts.authTokenCache.remove(account);
Carlos Valdivia91979be2015-05-22 14:11:35 -07001180 accounts.accountTokenCaches.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08001181 } else {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001182 ArrayList<String> accountNames = accountNamesByType.get(account.type);
Fred Quintana56285a62010-12-02 14:20:51 -08001183 if (accountNames == null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001184 accountNames = new ArrayList<>();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001185 accountNamesByType.put(account.type, accountNames);
Fred Quintana56285a62010-12-02 14:20:51 -08001186 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001187 accountNames.add(account.name);
Fred Quintana56285a62010-12-02 14:20:51 -08001188 }
1189 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001190 for (Map.Entry<String, ArrayList<String>> cur : accountNamesByType.entrySet()) {
Fred Quintana56285a62010-12-02 14:20:51 -08001191 final String accountType = cur.getKey();
1192 final ArrayList<String> accountNames = cur.getValue();
1193 final Account[] accountsForType = new Account[accountNames.size()];
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001194 for (int i = 0; i < accountsForType.length; i++) {
Svet Ganovf6d424f12016-09-20 20:18:53 -07001195 accountsForType[i] = new Account(accountNames.get(i), accountType,
1196 new AccountAccessTracker());
Fred Quintana56285a62010-12-02 14:20:51 -08001197 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001198 accounts.accountCache.put(accountType, accountsForType);
Fred Quintanaafa92b82009-12-01 16:27:03 -08001199 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001200 } finally {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001201 if (accountDeleted) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001202 sendAccountsChangedBroadcast(accounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001203 }
Fred Quintanaafa92b82009-12-01 16:27:03 -08001204 }
1205 }
Fred Quintana3ecd5f42009-09-17 12:42:35 -07001206 }
1207
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001208 private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
1209 // Get the UIDs of all apps that might have data on the device. We want
1210 // to preserve user data if the app might otherwise be storing data.
1211 List<PackageInfo> pkgsWithData =
1212 mPackageManager.getInstalledPackagesAsUser(
1213 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
1214 SparseBooleanArray knownUids = new SparseBooleanArray(pkgsWithData.size());
1215 for (PackageInfo pkgInfo : pkgsWithData) {
1216 if (pkgInfo.applicationInfo != null
1217 && (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
1218 knownUids.put(pkgInfo.applicationInfo.uid, true);
1219 }
1220 }
1221 return knownUids;
1222 }
1223
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001224 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1225 Context context,
1226 int userId) {
1227 AccountAuthenticatorCache authCache = new AccountAuthenticatorCache(context);
Carlos Valdiviaa46b1122016-04-26 19:36:50 -07001228 return getAuthenticatorTypeAndUIDForUser(authCache, userId);
1229 }
1230
1231 private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
1232 IAccountAuthenticatorCache authCache,
1233 int userId) {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08001234 HashMap<String, Integer> knownAuth = new HashMap<>();
1235 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
1236 .getAllServices(userId)) {
1237 knownAuth.put(service.type.type, service.uid);
1238 }
1239 return knownAuth;
1240 }
1241
Amith Yamasani04e0d262012-02-14 11:50:53 -08001242 private UserAccounts getUserAccountsForCaller() {
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07001243 return getUserAccounts(UserHandle.getCallingUserId());
Amith Yamasani04e0d262012-02-14 11:50:53 -08001244 }
1245
1246 protected UserAccounts getUserAccounts(int userId) {
1247 synchronized (mUsers) {
1248 UserAccounts accounts = mUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001249 boolean validateAccounts = false;
Amith Yamasani04e0d262012-02-14 11:50:53 -08001250 if (accounts == null) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001251 File preNDbFile = new File(getPreNDatabaseName(userId));
1252 File deDbFile = new File(getDeDatabaseName(userId));
1253 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001254 initializeDebugDbSizeAndCompileSqlStatementForLogging(
1255 accounts.openHelper.getWritableDatabase(), accounts);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001256 mUsers.append(userId, accounts);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001257 purgeOldGrants(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001258 validateAccounts = true;
1259 }
1260 // open CE database if necessary
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001261 if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001262 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
1263 synchronized (accounts.cacheLock) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001264 File preNDatabaseFile = new File(getPreNDatabaseName(userId));
1265 File ceDatabaseFile = new File(getCeDatabaseName(userId));
1266 CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
1267 accounts.openHelper.attachCeDatabase(ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001268 }
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001269 syncDeCeAccountsLocked(accounts);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001270 }
1271 if (validateAccounts) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001272 validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001273 }
1274 return accounts;
1275 }
1276 }
1277
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001278 private void syncDeCeAccountsLocked(UserAccounts accounts) {
1279 Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
1280 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001281 List<Account> accountsToRemove = AccountsDbUtils.findCeAccountsNotInDe(db);
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001282 if (!accountsToRemove.isEmpty()) {
1283 Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
1284 + accounts.userId + " was locked. Removing accounts from CE tables");
1285 logRecord(accounts, DebugDbHelper.ACTION_SYNC_DE_CE_ACCOUNTS, TABLE_ACCOUNTS);
1286
1287 for (Account account : accountsToRemove) {
1288 removeAccountInternal(accounts, account, Process.myUid());
1289 }
1290 }
1291 }
1292
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001293 private void purgeOldGrantsAll() {
1294 synchronized (mUsers) {
1295 for (int i = 0; i < mUsers.size(); i++) {
1296 purgeOldGrants(mUsers.valueAt(i));
1297 }
1298 }
1299 }
1300
1301 private void purgeOldGrants(UserAccounts accounts) {
1302 synchronized (accounts.cacheLock) {
1303 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001304 List<Integer> uids = AccountsDbUtils.findAllUidGrants(db);
1305 for (int uid : uids) {
1306 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
1307 if (packageExists) {
1308 continue;
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001309 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001310 Log.d(TAG, "deleting grants for UID " + uid
1311 + " because its package is no longer installed");
1312 AccountsDbUtils.deleteGrantsByUid(db, uid);
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07001313 }
1314 }
1315 }
1316
Amith Yamasani13593602012-03-22 16:16:17 -07001317 private void onUserRemoved(Intent intent) {
Amith Yamasani2a003292012-08-14 18:25:45 -07001318 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Amith Yamasani13593602012-03-22 16:16:17 -07001319 if (userId < 1) return;
1320
1321 UserAccounts accounts;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001322 boolean userUnlocked;
Amith Yamasani13593602012-03-22 16:16:17 -07001323 synchronized (mUsers) {
1324 accounts = mUsers.get(userId);
1325 mUsers.remove(userId);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001326 userUnlocked = mLocalUnlockedUsers.get(userId);
1327 mLocalUnlockedUsers.delete(userId);
Amith Yamasani13593602012-03-22 16:16:17 -07001328 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001329 if (accounts != null) {
1330 synchronized (accounts.cacheLock) {
1331 accounts.openHelper.close();
1332 }
Amith Yamasani13593602012-03-22 16:16:17 -07001333 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001334 Log.i(TAG, "Removing database files for user " + userId);
1335 File dbFile = new File(getDeDatabaseName(userId));
Amith Yamasani13593602012-03-22 16:16:17 -07001336
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001337 deleteDbFileWarnIfFailed(dbFile);
1338 // Remove CE file if user is unlocked, or FBE is not enabled
1339 boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
1340 if (!fbeEnabled || userUnlocked) {
1341 File ceDb = new File(getCeDatabaseName(userId));
1342 if (ceDb.exists()) {
1343 deleteDbFileWarnIfFailed(ceDb);
1344 }
1345 }
1346 }
1347
1348 private static void deleteDbFileWarnIfFailed(File dbFile) {
1349 if (!SQLiteDatabase.deleteDatabase(dbFile)) {
1350 Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
Amith Yamasani13593602012-03-22 16:16:17 -07001351 }
1352 }
1353
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07001354 @VisibleForTesting
1355 void onUserUnlocked(Intent intent) {
Jeff Sharkey1cab76a2016-04-12 18:23:31 -06001356 onUnlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
1357 }
1358
1359 void onUnlockUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001360 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1361 Log.v(TAG, "onUserUnlocked " + userId);
1362 }
1363 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001364 mLocalUnlockedUsers.put(userId, true);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001365 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001366 if (userId < 1) return;
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001367 syncSharedAccounts(userId);
1368 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001369
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001370 private void syncSharedAccounts(int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001371 // Check if there's a shared account that needs to be created as an account
1372 Account[] sharedAccounts = getSharedAccountsAsUser(userId);
1373 if (sharedAccounts == null || sharedAccounts.length == 0) return;
Svetoslavf3f02ac2015-09-08 14:36:35 -07001374 Account[] accounts = getAccountsAsUser(null, userId, mContext.getOpPackageName());
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001375 int parentUserId = UserManager.isSplitSystemUser()
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07001376 ? getUserManager().getUserInfo(userId).restrictedProfileParentId
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001377 : UserHandle.USER_SYSTEM;
1378 if (parentUserId < 0) {
1379 Log.w(TAG, "User " + userId + " has shared accounts, but no parent user");
1380 return;
1381 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001382 for (Account sa : sharedAccounts) {
1383 if (ArrayUtils.contains(accounts, sa)) continue;
1384 // Account doesn't exist. Copy it now.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001385 copyAccountToUser(null /*no response*/, sa, parentUserId, userId);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001386 }
1387 }
1388
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001389 @Override
1390 public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
Jeff Sharkey6eb96202012-10-10 13:13:54 -07001391 validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
Fred Quintana60307342009-03-24 22:48:12 -07001392 }
1393
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001394 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07001395 public String getPassword(Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001396 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001397 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1398 Log.v(TAG, "getPassword: " + account
1399 + ", caller's uid " + Binder.getCallingUid()
1400 + ", pid " + Binder.getCallingPid());
1401 }
Fred Quintana382601f2010-03-25 12:25:10 -07001402 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001403 int userId = UserHandle.getCallingUserId();
1404 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001405 String msg = String.format(
1406 "uid %s cannot get secrets for accounts of type: %s",
1407 callingUid,
1408 account.type);
1409 throw new SecurityException(msg);
1410 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001411 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001412 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001413 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001414 return readPasswordInternal(accounts, account);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001415 } finally {
1416 restoreCallingIdentity(identityToken);
1417 }
1418 }
1419
Amith Yamasani04e0d262012-02-14 11:50:53 -08001420 private String readPasswordInternal(UserAccounts accounts, Account account) {
Fred Quintana31957f12009-10-21 13:43:10 -07001421 if (account == null) {
1422 return null;
1423 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001424 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001425 Log.w(TAG, "Password is not available - user " + accounts.userId + " data is locked");
1426 return null;
1427 }
Fred Quintana31957f12009-10-21 13:43:10 -07001428
Amith Yamasani04e0d262012-02-14 11:50:53 -08001429 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001430 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001431 return AccountsDbUtils.findAccountPasswordByNameAndType(db, account.name,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001432 account.type);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001433 }
1434 }
1435
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001436 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001437 public String getPreviousName(Account account) {
1438 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1439 Log.v(TAG, "getPreviousName: " + account
1440 + ", caller's uid " + Binder.getCallingUid()
1441 + ", pid " + Binder.getCallingPid());
1442 }
1443 if (account == null) throw new IllegalArgumentException("account is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001444 int userId = UserHandle.getCallingUserId();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001445 long identityToken = clearCallingIdentity();
1446 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001447 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001448 return readPreviousNameInternal(accounts, account);
1449 } finally {
1450 restoreCallingIdentity(identityToken);
1451 }
1452 }
1453
1454 private String readPreviousNameInternal(UserAccounts accounts, Account account) {
1455 if (account == null) {
1456 return null;
1457 }
1458 synchronized (accounts.cacheLock) {
1459 AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
1460 if (previousNameRef == null) {
1461 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001462 String previousName = AccountsDbUtils.findAccountPreviousName(db, account);
1463 previousNameRef = new AtomicReference<>(previousName);
1464 accounts.previousNameCache.put(account, previousNameRef);
1465 return previousName;
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001466 } else {
1467 return previousNameRef.get();
1468 }
1469 }
1470 }
1471
1472 @Override
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001473 public String getUserData(Account account, String key) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001474 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001475 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001476 String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
1477 account, key, callingUid, Binder.getCallingPid());
1478 Log.v(TAG, msg);
Fred Quintana56285a62010-12-02 14:20:51 -08001479 }
Fred Quintana382601f2010-03-25 12:25:10 -07001480 if (account == null) throw new IllegalArgumentException("account is null");
1481 if (key == null) throw new IllegalArgumentException("key is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001482 int userId = UserHandle.getCallingUserId();
1483 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001484 String msg = String.format(
1485 "uid %s cannot get user data for accounts of type: %s",
1486 callingUid,
1487 account.type);
1488 throw new SecurityException(msg);
1489 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001490 if (!isLocalUnlockedUser(userId)) {
Fyodor Kupolovc86c3fd2016-04-18 13:57:31 -07001491 Log.w(TAG, "User " + userId + " data is locked. callingUid " + callingUid);
1492 return null;
1493 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001494 long identityToken = clearCallingIdentity();
1495 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001496 UserAccounts accounts = getUserAccounts(userId);
Simranjit Kohli858511c2016-03-10 18:36:11 +00001497 synchronized (accounts.cacheLock) {
1498 if (!accountExistsCacheLocked(accounts, account)) {
1499 return null;
1500 }
1501 return readUserDataInternalLocked(accounts, account, key);
1502 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001503 } finally {
1504 restoreCallingIdentity(identityToken);
1505 }
1506 }
1507
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001508 @Override
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001509 public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001510 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001511 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1512 Log.v(TAG, "getAuthenticatorTypes: "
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001513 + "for user id " + userId
Fyodor Kupolov35f68082016-04-06 12:14:17 -07001514 + " caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001515 + ", pid " + Binder.getCallingPid());
1516 }
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001517 // Only allow the system process to read accounts of other users
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001518 if (isCrossUser(callingUid, userId)) {
1519 throw new SecurityException(
1520 String.format(
1521 "User %s tying to get authenticator types for %s" ,
1522 UserHandle.getCallingUserId(),
1523 userId));
1524 }
1525
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07001526 final long identityToken = clearCallingIdentity();
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001527 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001528 return getAuthenticatorTypesInternal(userId);
1529
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001530 } finally {
1531 restoreCallingIdentity(identityToken);
Fred Quintanaa698f422009-04-08 19:14:54 -07001532 }
Fred Quintanaa698f422009-04-08 19:14:54 -07001533 }
1534
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001535 /**
1536 * Should only be called inside of a clearCallingIdentity block.
1537 */
1538 private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) {
Fyodor Kupolov81446482016-08-24 11:27:49 -07001539 mAuthenticatorCache.updateServices(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001540 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
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001553 private boolean isCrossUser(int callingUid, int userId) {
1554 return (userId != UserHandle.getCallingUserId()
1555 && callingUid != Process.myUid()
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001556 && mContext.checkCallingOrSelfPermission(
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001557 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
1558 != PackageManager.PERMISSION_GRANTED);
Alexandra Gherghinac1cf1612014-06-05 10:49:14 +01001559 }
1560
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001561 @Override
Amith Yamasani27db4682013-03-30 17:07:47 -07001562 public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001563 Bundle.setDefusable(extras, true);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001564 // clears the visible list functionality for this account because this method allows
1565 // default account access to all applications for account.
1566
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001567 final int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001568 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Amith Yamasani27db4682013-03-30 17:07:47 -07001569 Log.v(TAG, "addAccountExplicitly: " + account
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001570 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001571 + ", pid " + Binder.getCallingPid());
1572 }
Fred Quintana382601f2010-03-25 12:25:10 -07001573 if (account == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001574 int userId = UserHandle.getCallingUserId();
1575 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001576 String msg = String.format(
1577 "uid %s cannot explicitly add accounts of type: %s",
1578 callingUid,
1579 account.type);
1580 throw new SecurityException(msg);
1581 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07001582 removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
Jatin Lodhia3df7d692013-03-27 10:57:23 -07001583 /*
1584 * Child users are not allowed to add accounts. Only the accounts that are
1585 * shared by the parent profile can be added to child profile.
1586 *
1587 * TODO: Only allow accounts that were shared to be added by
1588 * a limited user.
1589 */
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07001590
Fred Quintana60307342009-03-24 22:48:12 -07001591 // fails if the account already exists
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001592 long identityToken = clearCallingIdentity();
Fred Quintana60307342009-03-24 22:48:12 -07001593 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001594 UserAccounts accounts = getUserAccounts(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001595 return addAccountInternal(accounts, account, password, extras, callingUid);
Fred Quintana60307342009-03-24 22:48:12 -07001596 } finally {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07001597 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07001598 }
1599 }
1600
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001601 @Override
1602 public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001603 final int userFrom, int userTo) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001604 int callingUid = Binder.getCallingUid();
1605 if (isCrossUser(callingUid, UserHandle.USER_ALL)) {
1606 throw new SecurityException("Calling copyAccountToUser requires "
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001607 + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001608 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001609 final UserAccounts fromAccounts = getUserAccounts(userFrom);
1610 final UserAccounts toAccounts = getUserAccounts(userTo);
1611 if (fromAccounts == null || toAccounts == null) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001612 if (response != null) {
1613 Bundle result = new Bundle();
1614 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
1615 try {
1616 response.onResult(result);
1617 } catch (RemoteException e) {
1618 Slog.w(TAG, "Failed to report error back to the client." + e);
1619 }
1620 }
1621 return;
Amith Yamasani67df64b2012-12-14 12:09:36 -08001622 }
1623
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001624 Slog.d(TAG, "Copying account " + account.name
1625 + " from user " + userFrom + " to user " + userTo);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001626 long identityToken = clearCallingIdentity();
1627 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001628 new Session(fromAccounts, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001629 false /* stripAuthTokenFromResult */, account.name,
1630 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001631 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001632 protected String toDebugString(long now) {
1633 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1634 + ", " + account.type;
1635 }
1636
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001637 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001638 public void run() throws RemoteException {
1639 mAuthenticator.getAccountCredentialsForCloning(this, account);
1640 }
1641
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001642 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001643 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001644 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001645 if (result != null
1646 && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
1647 // Create a Session for the target user and pass in the bundle
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001648 completeCloningAccount(response, result, account, toAccounts, userFrom);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001649 } else {
Amith Yamasani67df64b2012-12-14 12:09:36 -08001650 super.onResult(result);
1651 }
1652 }
1653 }.bind();
1654 } finally {
1655 restoreCallingIdentity(identityToken);
1656 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001657 }
1658
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001659 @Override
1660 public boolean accountAuthenticated(final Account account) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001661 final int callingUid = Binder.getCallingUid();
1662 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1663 String msg = String.format(
1664 "accountAuthenticated( account: %s, callerUid: %s)",
1665 account,
1666 callingUid);
1667 Log.v(TAG, msg);
1668 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001669 if (account == null) {
1670 throw new IllegalArgumentException("account is null");
1671 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001672 int userId = UserHandle.getCallingUserId();
1673 if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001674 String msg = String.format(
1675 "uid %s cannot notify authentication for accounts of type: %s",
1676 callingUid,
1677 account.type);
1678 throw new SecurityException(msg);
1679 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001680
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00001681 if (!canUserModifyAccounts(userId, callingUid) ||
1682 !canUserModifyAccountsForType(userId, account.type, callingUid)) {
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001683 return false;
1684 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001685
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001686 long identityToken = clearCallingIdentity();
1687 try {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001688 UserAccounts accounts = getUserAccounts(userId);
1689 return updateLastAuthenticatedTime(account);
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001690 } finally {
1691 restoreCallingIdentity(identityToken);
1692 }
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07001693 }
1694
1695 private boolean updateLastAuthenticatedTime(Account account) {
1696 final UserAccounts accounts = getUserAccountsForCaller();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001697 synchronized (accounts.cacheLock) {
1698 final ContentValues values = new ContentValues();
1699 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
1700 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
1701 int i = db.update(
1702 TABLE_ACCOUNTS,
1703 values,
1704 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
1705 new String[] {
1706 account.name, account.type
1707 });
1708 if (i > 0) {
1709 return true;
1710 }
1711 }
1712 return false;
1713 }
1714
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001715 private void completeCloningAccount(IAccountManagerResponse response,
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001716 final Bundle accountCredentials, final Account account, final UserAccounts targetUser,
1717 final int parentUserId){
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001718 Bundle.setDefusable(accountCredentials, true);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001719 long id = clearCallingIdentity();
1720 try {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001721 new Session(targetUser, response, account.type, false,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001722 false /* stripAuthTokenFromResult */, account.name,
1723 false /* authDetailsRequired */) {
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001724 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001725 protected String toDebugString(long now) {
1726 return super.toDebugString(now) + ", getAccountCredentialsForClone"
1727 + ", " + account.type;
1728 }
1729
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001730 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001731 public void run() throws RemoteException {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001732 // Confirm that the owner's account still exists before this step.
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001733 UserAccounts owner = getUserAccounts(parentUserId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001734 synchronized (owner.cacheLock) {
Svetoslavf3f02ac2015-09-08 14:36:35 -07001735 for (Account acc : getAccounts(parentUserId,
1736 mContext.getOpPackageName())) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001737 if (acc.equals(account)) {
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001738 mAuthenticator.addAccountFromCredentials(
1739 this, account, accountCredentials);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001740 break;
1741 }
1742 }
1743 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08001744 }
1745
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001746 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001747 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001748 Bundle.setDefusable(result, true);
Esteban Talavera22dc3b72014-10-31 15:41:12 +00001749 // TODO: Anything to do if if succedded?
1750 // TODO: If it failed: Show error notification? Should we remove the shadow
1751 // account to avoid retries?
1752 super.onResult(result);
Amith Yamasani67df64b2012-12-14 12:09:36 -08001753 }
1754
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001755 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08001756 public void onError(int errorCode, String errorMessage) {
1757 super.onError(errorCode, errorMessage);
1758 // TODO: Show error notification to user
1759 // TODO: Should we remove the shadow account so that it doesn't keep trying?
1760 }
1761
1762 }.bind();
1763 } finally {
1764 restoreCallingIdentity(id);
1765 }
1766 }
1767
Amith Yamasani04e0d262012-02-14 11:50:53 -08001768 private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001769 Bundle extras, int callingUid) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001770 Bundle.setDefusable(extras, true);
Fred Quintana743dfad2010-07-15 10:59:25 -07001771 if (account == null) {
1772 return false;
1773 }
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001774 if (!isLocalUnlockedUser(accounts.userId)) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001775 Log.w(TAG, "Account " + account + " cannot be added - user " + accounts.userId
1776 + " is locked. callingUid=" + callingUid);
1777 return false;
1778 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08001779 synchronized (accounts.cacheLock) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001780 final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001781 db.beginTransaction();
1782 try {
1783 long numMatches = DatabaseUtils.longForQuery(db,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001784 "select count(*) from " + CE_TABLE_ACCOUNTS
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001785 + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
1786 new String[]{account.name, account.type});
1787 if (numMatches > 0) {
1788 Log.w(TAG, "insertAccountIntoDatabase: " + account
1789 + ", skipping since the account already exists");
1790 return false;
1791 }
1792 ContentValues values = new ContentValues();
1793 values.put(ACCOUNTS_NAME, account.name);
1794 values.put(ACCOUNTS_TYPE, account.type);
1795 values.put(ACCOUNTS_PASSWORD, password);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001796 long accountId = db.insert(CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001797 if (accountId < 0) {
1798 Log.w(TAG, "insertAccountIntoDatabase: " + account
1799 + ", skipping the DB insert failed");
1800 return false;
1801 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001802 // Insert into DE table
1803 values = new ContentValues();
1804 values.put(ACCOUNTS_ID, accountId);
1805 values.put(ACCOUNTS_NAME, account.name);
1806 values.put(ACCOUNTS_TYPE, account.type);
1807 values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS,
1808 System.currentTimeMillis());
1809 if (db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values) < 0) {
1810 Log.w(TAG, "insertAccountIntoDatabase: " + account
1811 + ", skipping the DB insert failed");
1812 return false;
1813 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001814 if (extras != null) {
1815 for (String key : extras.keySet()) {
1816 final String value = extras.getString(key);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07001817 if (AccountsDbUtils.insertExtra(db, accountId, key, value) < 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001818 Log.w(TAG, "insertAccountIntoDatabase: " + account
1819 + ", skipping since insertExtra failed for key " + key);
1820 return false;
1821 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001822 }
1823 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001824 db.setTransactionSuccessful();
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07001825
1826 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_ACCOUNTS, accountId,
1827 accounts, callingUid);
1828
Amith Yamasani04e0d262012-02-14 11:50:53 -08001829 insertAccountIntoCacheLocked(accounts, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08001830 } finally {
1831 db.endTransaction();
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001832 }
Amith Yamasani5be347b2013-03-31 17:44:31 -07001833 }
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001834 if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
1835 addAccountToLinkedRestrictedUsers(account, accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001836 }
Carlos Valdivia98b5f9d2016-07-07 17:47:12 -07001837
1838 // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
1839 sendAccountsChangedBroadcast(accounts.userId);
Amith Yamasani5be347b2013-03-31 17:44:31 -07001840 return true;
1841 }
1842
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001843 private boolean isLocalUnlockedUser(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001844 synchronized (mUsers) {
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001845 return mLocalUnlockedUsers.get(userId);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07001846 }
1847 }
1848
Amith Yamasani5be347b2013-03-31 17:44:31 -07001849 /**
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001850 * Adds the account to all linked restricted users as shared accounts. If the user is currently
Amith Yamasani5be347b2013-03-31 17:44:31 -07001851 * running, then clone the account too.
1852 * @param account the account to share with limited users
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001853 *
Amith Yamasani5be347b2013-03-31 17:44:31 -07001854 */
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001855 private void addAccountToLinkedRestrictedUsers(Account account, int parentUserId) {
Mita Yunf4c240e2013-04-01 21:12:43 -07001856 List<UserInfo> users = getUserManager().getUsers();
Amith Yamasani5be347b2013-03-31 17:44:31 -07001857 for (UserInfo user : users) {
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07001858 if (user.isRestricted() && (parentUserId == user.restrictedProfileParentId)) {
Amith Yamasani5be347b2013-03-31 17:44:31 -07001859 addSharedAccountAsUser(account, user.id);
Jeff Sharkeyce18c812016-04-27 16:00:41 -06001860 if (isLocalUnlockedUser(user.id)) {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07001861 mHandler.sendMessage(mHandler.obtainMessage(
Fyodor Kupolov041232a2016-02-22 15:01:45 -08001862 MESSAGE_COPY_SHARED_ACCOUNT, parentUserId, user.id, account));
Amith Yamasani5be347b2013-03-31 17:44:31 -07001863 }
1864 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07001865 }
1866 }
1867
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001868 @Override
Fred Quintana3084a6f2010-01-14 18:02:03 -08001869 public void hasFeatures(IAccountManagerResponse response,
Svetoslavf3f02ac2015-09-08 14:36:35 -07001870 Account account, String[] features, String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001871 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08001872 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1873 Log.v(TAG, "hasFeatures: " + account
1874 + ", response " + response
1875 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001876 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08001877 + ", pid " + Binder.getCallingPid());
1878 }
Fred Quintana382601f2010-03-25 12:25:10 -07001879 if (response == null) throw new IllegalArgumentException("response is null");
1880 if (account == null) throw new IllegalArgumentException("account is null");
1881 if (features == null) throw new IllegalArgumentException("features is null");
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001882 int userId = UserHandle.getCallingUserId();
Svetoslavf3f02ac2015-09-08 14:36:35 -07001883 checkReadAccountsPermitted(callingUid, account.type, userId,
1884 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001885
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001886 long identityToken = clearCallingIdentity();
1887 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001888 UserAccounts accounts = getUserAccounts(userId);
Amith Yamasani04e0d262012-02-14 11:50:53 -08001889 new TestFeaturesSession(accounts, response, account, features).bind();
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001890 } finally {
1891 restoreCallingIdentity(identityToken);
1892 }
1893 }
1894
1895 private class TestFeaturesSession extends Session {
1896 private final String[] mFeatures;
1897 private final Account mAccount;
1898
Amith Yamasani04e0d262012-02-14 11:50:53 -08001899 public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001900 Account account, String[] features) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08001901 super(accounts, response, account.type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08001902 true /* stripAuthTokenFromResult */, account.name,
1903 false /* authDetailsRequired */);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001904 mFeatures = features;
1905 mAccount = account;
1906 }
1907
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001908 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001909 public void run() throws RemoteException {
1910 try {
1911 mAuthenticator.hasFeatures(this, mAccount, mFeatures);
1912 } catch (RemoteException e) {
1913 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
1914 }
1915 }
1916
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001917 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001918 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06001919 Bundle.setDefusable(result, true);
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001920 IAccountManagerResponse response = getResponseAndClose();
1921 if (response != null) {
1922 try {
1923 if (result == null) {
Fred Quintana166466d2011-10-24 14:51:40 -07001924 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001925 return;
1926 }
Fred Quintana56285a62010-12-02 14:20:51 -08001927 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1928 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
1929 + response);
1930 }
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001931 final Bundle newResult = new Bundle();
1932 newResult.putBoolean(AccountManager.KEY_BOOLEAN_RESULT,
1933 result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false));
1934 response.onResult(newResult);
1935 } catch (RemoteException e) {
1936 // if the caller is dead then there is no one to care about remote exceptions
1937 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1938 Log.v(TAG, "failure while notifying response", e);
1939 }
1940 }
1941 }
1942 }
1943
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07001944 @Override
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001945 protected String toDebugString(long now) {
Fred Quintana3084a6f2010-01-14 18:02:03 -08001946 return super.toDebugString(now) + ", hasFeatures"
Fred Quintanabb68a4f2010-01-13 17:28:39 -08001947 + ", " + mAccount
1948 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
1949 }
1950 }
Fred Quintana307da1a2010-01-21 14:24:20 -08001951
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08001952 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001953 public void renameAccount(
1954 IAccountManagerResponse response, Account accountToRename, String newName) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001955 final int callingUid = Binder.getCallingUid();
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001956 if (Log.isLoggable(TAG, Log.VERBOSE)) {
1957 Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001958 + ", caller's uid " + callingUid
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001959 + ", pid " + Binder.getCallingPid());
1960 }
1961 if (accountToRename == null) throw new IllegalArgumentException("account is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00001962 int userId = UserHandle.getCallingUserId();
1963 if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) {
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07001964 String msg = String.format(
1965 "uid %s cannot rename accounts of type: %s",
1966 callingUid,
1967 accountToRename.type);
1968 throw new SecurityException(msg);
1969 }
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001970 long identityToken = clearCallingIdentity();
1971 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07001972 UserAccounts accounts = getUserAccounts(userId);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07001973 Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001974 Bundle result = new Bundle();
1975 result.putString(AccountManager.KEY_ACCOUNT_NAME, resultingAccount.name);
1976 result.putString(AccountManager.KEY_ACCOUNT_TYPE, resultingAccount.type);
Svet Ganovf6d424f12016-09-20 20:18:53 -07001977 result.putBinder(AccountManager.KEY_ACCOUNT_ACCESS_TRACKER,
1978 resultingAccount.getAccessTracker().asBinder());
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07001979 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)
Svet Ganovf6d424f12016-09-20 20:18:53 -07002326 && AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE.equals(key.first.second)) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07002327 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();
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07002706 if (UserHandle.getAppId(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) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003877 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003878 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 {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003889 final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
Svet Ganovf6d424f12016-09-20 20:18:53 -07003890 return hasAccountAccess(account, packageName, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003891 } catch (NameNotFoundException e) {
3892 return false;
3893 }
3894 }
3895
Svet Ganovf6d424f12016-09-20 20:18:53 -07003896 private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
3897 int uid) {
3898 if (packageName == null) {
3899 String[] packageNames = mPackageManager.getPackagesForUid(uid);
3900 if (ArrayUtils.isEmpty(packageNames)) {
3901 return false;
3902 }
3903 // For app op checks related to permissions all packages in the UID
3904 // have the same app op state, so doesn't matter which one we pick.
3905 packageName = packageNames[0];
3906 }
3907
3908 // Use null token which means any token. Having a token means the package
3909 // is trusted by the authenticator, hence it is fine to access the account.
3910 if (permissionIsGranted(account, null, uid, UserHandle.getUserId(uid))) {
3911 return true;
3912 }
3913 // In addition to the permissions required to get an auth token we also allow
3914 // the account to be accessed by holders of the get accounts permissions.
3915 return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
3916 || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
3917 }
3918
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003919 private boolean checkUidPermission(String permission, int uid, String opPackageName) {
3920 final long identity = Binder.clearCallingIdentity();
3921 try {
3922 IPackageManager pm = ActivityThread.getPackageManager();
3923 if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
3924 return false;
3925 }
3926 final int opCode = AppOpsManager.permissionToOpCode(permission);
3927 return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
3928 opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
3929 } catch (RemoteException e) {
3930 /* ignore - local call */
3931 } finally {
3932 Binder.restoreCallingIdentity(identity);
3933 }
3934 return false;
3935 }
3936
3937 @Override
3938 public IntentSender createRequestAccountAccessIntentSenderAsUser(@NonNull Account account,
3939 @NonNull String packageName, @NonNull UserHandle userHandle) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003940 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003941 throw new SecurityException("Can be called only by system UID");
3942 }
3943
3944 Preconditions.checkNotNull(account, "account cannot be null");
3945 Preconditions.checkNotNull(packageName, "packageName cannot be null");
3946 Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
3947
3948 final int userId = userHandle.getIdentifier();
3949
3950 Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
3951
3952 final int uid;
3953 try {
3954 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
3955 } catch (NameNotFoundException e) {
3956 Slog.e(TAG, "Unknown package " + packageName);
3957 return null;
3958 }
3959
3960 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, null);
3961
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07003962 final long identity = Binder.clearCallingIdentity();
3963 try {
3964 return PendingIntent.getActivityAsUser(
3965 mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT
3966 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
3967 null, new UserHandle(userId)).getIntentSender();
3968 } finally {
3969 Binder.restoreCallingIdentity(identity);
3970 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003971 }
3972
3973 private Intent newRequestAccountAccessIntent(Account account, String packageName,
3974 int uid, RemoteCallback callback) {
3975 return newGrantCredentialsPermissionIntent(account, packageName, uid,
3976 new AccountAuthenticatorResponse(new IAccountAuthenticatorResponse.Stub() {
3977 @Override
3978 public void onResult(Bundle value) throws RemoteException {
3979 handleAuthenticatorResponse(true);
3980 }
3981
3982 @Override
3983 public void onRequestContinued() {
3984 /* ignore */
3985 }
3986
3987 @Override
3988 public void onError(int errorCode, String errorMessage) throws RemoteException {
3989 handleAuthenticatorResponse(false);
3990 }
3991
3992 private void handleAuthenticatorResponse(boolean accessGranted) throws RemoteException {
3993 cancelNotification(getCredentialPermissionNotificationId(account,
Svet Ganovf6d424f12016-09-20 20:18:53 -07003994 AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid), packageName,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07003995 UserHandle.getUserHandleForUid(uid));
3996 if (callback != null) {
3997 Bundle result = new Bundle();
3998 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, accessGranted);
3999 callback.sendResult(result);
4000 }
4001 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07004002 }), AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, false);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07004003 }
4004
4005 @Override
Amith Yamasani12747872015-12-07 14:19:49 -08004006 public boolean someUserHasAccount(@NonNull final Account account) {
4007 if (!UserHandle.isSameApp(Process.SYSTEM_UID, Binder.getCallingUid())) {
4008 throw new SecurityException("Only system can check for accounts across users");
4009 }
4010 final long token = Binder.clearCallingIdentity();
4011 try {
4012 AccountAndUser[] allAccounts = getAllAccounts();
4013 for (int i = allAccounts.length - 1; i >= 0; i--) {
4014 if (allAccounts[i].account.equals(account)) {
4015 return true;
4016 }
4017 }
4018 return false;
4019 } finally {
4020 Binder.restoreCallingIdentity(token);
4021 }
4022 }
4023
Fred Quintana33269202009-04-20 16:05:10 -07004024 private class GetAccountsByTypeAndFeatureSession extends Session {
4025 private final String[] mFeatures;
4026 private volatile Account[] mAccountsOfType = null;
4027 private volatile ArrayList<Account> mAccountsWithFeatures = null;
4028 private volatile int mCurrentAccount = 0;
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004029 private final int mCallingUid;
Fred Quintana33269202009-04-20 16:05:10 -07004030
Amith Yamasani04e0d262012-02-14 11:50:53 -08004031 public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004032 IAccountManagerResponse response, String type, String[] features, int callingUid) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004033 super(accounts, response, type, false /* expectActivityLaunch */,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004034 true /* stripAuthTokenFromResult */, null /* accountName */,
4035 false /* authDetailsRequired */);
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004036 mCallingUid = callingUid;
Fred Quintana33269202009-04-20 16:05:10 -07004037 mFeatures = features;
4038 }
4039
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004040 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004041 public void run() throws RemoteException {
Amith Yamasani04e0d262012-02-14 11:50:53 -08004042 synchronized (mAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004043 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
4044 null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004045 }
Fred Quintana33269202009-04-20 16:05:10 -07004046 // check whether each account matches the requested features
Tejas Khorana5edff3b2016-06-28 20:59:52 -07004047 mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
Fred Quintana33269202009-04-20 16:05:10 -07004048 mCurrentAccount = 0;
4049
4050 checkAccount();
4051 }
4052
4053 public void checkAccount() {
4054 if (mCurrentAccount >= mAccountsOfType.length) {
4055 sendResult();
4056 return;
Fred Quintanaa698f422009-04-08 19:14:54 -07004057 }
Fred Quintana33269202009-04-20 16:05:10 -07004058
Fred Quintana29e94b82010-03-10 12:11:51 -08004059 final IAccountAuthenticator accountAuthenticator = mAuthenticator;
4060 if (accountAuthenticator == null) {
4061 // It is possible that the authenticator has died, which is indicated by
4062 // mAuthenticator being set to null. If this happens then just abort.
4063 // There is no need to send back a result or error in this case since
4064 // that already happened when mAuthenticator was cleared.
4065 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4066 Log.v(TAG, "checkAccount: aborting session since we are no longer"
4067 + " connected to the authenticator, " + toDebugString());
4068 }
4069 return;
4070 }
Fred Quintana33269202009-04-20 16:05:10 -07004071 try {
Fred Quintana29e94b82010-03-10 12:11:51 -08004072 accountAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
Fred Quintana33269202009-04-20 16:05:10 -07004073 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004074 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
Fred Quintana33269202009-04-20 16:05:10 -07004075 }
4076 }
4077
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004078 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004079 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004080 Bundle.setDefusable(result, true);
Fred Quintana33269202009-04-20 16:05:10 -07004081 mNumResults++;
4082 if (result == null) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004083 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
Fred Quintana33269202009-04-20 16:05:10 -07004084 return;
4085 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004086 if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
Fred Quintana33269202009-04-20 16:05:10 -07004087 mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
4088 }
4089 mCurrentAccount++;
4090 checkAccount();
4091 }
4092
4093 public void sendResult() {
4094 IAccountManagerResponse response = getResponseAndClose();
4095 if (response != null) {
4096 try {
4097 Account[] accounts = new Account[mAccountsWithFeatures.size()];
4098 for (int i = 0; i < accounts.length; i++) {
4099 accounts[i] = mAccountsWithFeatures.get(i);
4100 }
Fred Quintana56285a62010-12-02 14:20:51 -08004101 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4102 Log.v(TAG, getClass().getSimpleName() + " calling onResult() on response "
4103 + response);
4104 }
Fred Quintana33269202009-04-20 16:05:10 -07004105 Bundle result = new Bundle();
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004106 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
Fred Quintana33269202009-04-20 16:05:10 -07004107 response.onResult(result);
4108 } catch (RemoteException e) {
4109 // if the caller is dead then there is no one to care about remote exceptions
4110 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4111 Log.v(TAG, "failure while notifying response", e);
4112 }
4113 }
4114 }
4115 }
4116
4117
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004118 @Override
Fred Quintana33269202009-04-20 16:05:10 -07004119 protected String toDebugString(long now) {
4120 return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
4121 + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
4122 }
4123 }
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004124
Amith Yamasani04e0d262012-02-14 11:50:53 -08004125 /**
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004126 * Returns the accounts visible to the client within the context of a specific user
Amith Yamasani04e0d262012-02-14 11:50:53 -08004127 * @hide
4128 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004129 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004130 public Account[] getAccounts(int userId, String opPackageName) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004131 int callingUid = Binder.getCallingUid();
Svetoslavf3f02ac2015-09-08 14:36:35 -07004132 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4133 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004134 if (visibleAccountTypes.isEmpty()) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004135 return new Account[0];
4136 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004137 long identityToken = clearCallingIdentity();
4138 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004139 UserAccounts accounts = getUserAccounts(userId);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004140 return getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004141 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004142 callingUid,
4143 null, // packageName
4144 visibleAccountTypes);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004145 } finally {
4146 restoreCallingIdentity(identityToken);
4147 }
4148 }
4149
Amith Yamasanif29f2362012-04-05 18:29:52 -07004150 /**
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004151 * Returns accounts for all running users.
4152 *
Amith Yamasanif29f2362012-04-05 18:29:52 -07004153 * @hide
4154 */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004155 @NonNull
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004156 public AccountAndUser[] getRunningAccounts() {
4157 final int[] runningUserIds;
4158 try {
4159 runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
4160 } catch (RemoteException e) {
4161 // Running in system_server; should never happen
4162 throw new RuntimeException(e);
4163 }
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004164 return getAccounts(runningUserIds);
4165 }
Amith Yamasanif29f2362012-04-05 18:29:52 -07004166
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004167 /** {@hide} */
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004168 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004169 public AccountAndUser[] getAllAccounts() {
Amith Yamasanid04aaa32016-06-13 12:09:36 -07004170 final List<UserInfo> users = getUserManager().getUsers(true);
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004171 final int[] userIds = new int[users.size()];
4172 for (int i = 0; i < userIds.length; i++) {
4173 userIds[i] = users.get(i).id;
4174 }
4175 return getAccounts(userIds);
4176 }
4177
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004178 @NonNull
Jeff Sharkey6eb96202012-10-10 13:13:54 -07004179 private AccountAndUser[] getAccounts(int[] userIds) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004180 final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
Amith Yamasani0c19bf52013-10-03 10:34:58 -07004181 for (int userId : userIds) {
4182 UserAccounts userAccounts = getUserAccounts(userId);
4183 if (userAccounts == null) continue;
4184 synchronized (userAccounts.cacheLock) {
4185 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
4186 Binder.getCallingUid(), null);
4187 for (int a = 0; a < accounts.length; a++) {
4188 runningAccounts.add(new AccountAndUser(accounts[a], userId));
Amith Yamasanif29f2362012-04-05 18:29:52 -07004189 }
4190 }
4191 }
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004192
4193 AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
4194 return runningAccounts.toArray(accountsArray);
Amith Yamasanif29f2362012-04-05 18:29:52 -07004195 }
4196
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004197 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004198 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004199 public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
4200 return getAccountsAsUser(type, userId, null, -1, opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004201 }
4202
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004203 @NonNull
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004204 private Account[] getAccountsAsUser(
4205 String type,
4206 int userId,
4207 String callingPackage,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004208 int packageUid,
4209 String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004210 int callingUid = Binder.getCallingUid();
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004211 // Only allow the system process to read accounts of other users
4212 if (userId != UserHandle.getCallingUserId()
Amith Yamasanibb49e852013-03-30 19:20:18 -07004213 && callingUid != Process.myUid()
Jim Miller464f5302013-02-27 18:33:25 -08004214 && mContext.checkCallingOrSelfPermission(
4215 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
4216 != PackageManager.PERMISSION_GRANTED) {
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004217 throw new SecurityException("User " + UserHandle.getCallingUserId()
4218 + " trying to get account for " + userId);
4219 }
4220
Fred Quintana56285a62010-12-02 14:20:51 -08004221 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4222 Log.v(TAG, "getAccounts: accountType " + type
4223 + ", caller's uid " + Binder.getCallingUid()
4224 + ", pid " + Binder.getCallingPid());
4225 }
Amith Yamasani27db4682013-03-30 17:07:47 -07004226 // If the original calling app was using the framework account chooser activity, we'll
4227 // be passed in the original caller's uid here, which is what should be used for filtering.
4228 if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
4229 callingUid = packageUid;
Svetoslav5579e412015-09-10 15:30:45 -07004230 opPackageName = callingPackage;
Amith Yamasani27db4682013-03-30 17:07:47 -07004231 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004232 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4233 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004234 if (visibleAccountTypes.isEmpty()
4235 || (type != null && !visibleAccountTypes.contains(type))) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004236 return new Account[0];
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004237 } else if (visibleAccountTypes.contains(type)) {
4238 // Prune the list down to just the requested type.
4239 visibleAccountTypes = new ArrayList<>();
4240 visibleAccountTypes.add(type);
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07004241 } // else aggregate all the visible accounts (it won't matter if the
4242 // list is empty).
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004243
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004244 long identityToken = clearCallingIdentity();
4245 try {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004246 UserAccounts accounts = getUserAccounts(userId);
Tejas Khorana69990d92016-08-03 11:19:40 -07004247 Account[] accountsToReturn = getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004248 accounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004249 callingUid,
4250 callingPackage,
4251 visibleAccountTypes);
Tejas Khorana69990d92016-08-03 11:19:40 -07004252 ArrayList<Account> accountsToReturnList = new
4253 ArrayList<Account>(Arrays.asList(accountsToReturn));
4254 for(int i = accountsToReturnList.size() - 1; i >= 0 ; i--) {
4255 // if account not visible to caller or managed by caller, remove from
4256 // accounts to return. Note that all accounts visible by default unless
4257 // visible list functionality implemented
4258 if(!(isAccountVisible(accountsToReturnList.get(i), callingUid,
4259 getUserAccounts(userId)))) {
4260 accountsToReturnList.remove(i);
4261 }
4262 }
4263 return accountsToReturnList.toArray(new Account[accountsToReturnList.size()]);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004264 } finally {
4265 restoreCallingIdentity(identityToken);
4266 }
4267 }
4268
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004269 @NonNull
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004270 private Account[] getAccountsInternal(
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004271 UserAccounts userAccounts,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004272 int callingUid,
4273 String callingPackage,
4274 List<String> visibleAccountTypes) {
Carlos Valdiviaa3721e12015-08-10 18:40:06 -07004275 synchronized (userAccounts.cacheLock) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004276 ArrayList<Account> visibleAccounts = new ArrayList<>();
4277 for (String visibleType : visibleAccountTypes) {
4278 Account[] accountsForType = getAccountsFromCacheLocked(
4279 userAccounts, visibleType, callingUid, callingPackage);
4280 if (accountsForType != null) {
4281 visibleAccounts.addAll(Arrays.asList(accountsForType));
4282 }
4283 }
4284 Account[] result = new Account[visibleAccounts.size()];
4285 for (int i = 0; i < visibleAccounts.size(); i++) {
4286 result[i] = visibleAccounts.get(i);
4287 }
4288 return result;
4289 }
4290 }
4291
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004292 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004293 public void addSharedAccountsFromParentUser(int parentUserId, int userId) {
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07004294 checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser");
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004295 Account[] accounts = getAccountsAsUser(null, parentUserId, mContext.getOpPackageName());
4296 for (Account account : accounts) {
4297 addSharedAccountAsUser(account, userId);
4298 }
4299 }
4300
4301 private boolean addSharedAccountAsUser(Account account, int userId) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004302 userId = handleIncomingUser(userId);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004303 UserAccounts accounts = getUserAccounts(userId);
4304 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004305 AccountsDbUtils.deleteSharedAccount(db, account);
4306 long accountId = AccountsDbUtils.insertSharedAccount(db, account);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004307 if (accountId < 0) {
4308 Log.w(TAG, "insertAccountIntoDatabase: " + account
4309 + ", skipping the DB insert failed");
4310 return false;
4311 }
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004312 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_ADD, TABLE_SHARED_ACCOUNTS, accountId, accounts);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004313 return true;
4314 }
4315
4316 @Override
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004317 public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
4318 userId = handleIncomingUser(userId);
4319 UserAccounts accounts = getUserAccounts(userId);
4320 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004321 long sharedTableAccountId = AccountsDbUtils.findSharedAccountId(db, account);
4322 int r = AccountsDbUtils.renameSharedAccount(db, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004323 if (r > 0) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004324 int callingUid = getCallingUid();
4325 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_RENAME, TABLE_SHARED_ACCOUNTS,
4326 sharedTableAccountId, accounts, callingUid);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004327 // Recursively rename the account.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004328 renameAccountInternal(accounts, account, newName);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07004329 }
4330 return r > 0;
4331 }
4332
4333 @Override
Amith Yamasani67df64b2012-12-14 12:09:36 -08004334 public boolean removeSharedAccountAsUser(Account account, int userId) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004335 return removeSharedAccountAsUser(account, userId, getCallingUid());
4336 }
4337
4338 private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
Amith Yamasani67df64b2012-12-14 12:09:36 -08004339 userId = handleIncomingUser(userId);
4340 UserAccounts accounts = getUserAccounts(userId);
4341 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004342 long sharedTableAccountId = AccountsDbUtils.findSharedAccountId(db, account);
4343 boolean deleted = AccountsDbUtils.deleteSharedAccount(db, account);
4344 if (deleted) {
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004345 logRecord(db, DebugDbHelper.ACTION_ACCOUNT_REMOVE, TABLE_SHARED_ACCOUNTS,
4346 sharedTableAccountId, accounts, callingUid);
Fyodor Kupolov06a484a2015-08-21 16:33:20 -07004347 removeAccountInternal(accounts, account, callingUid);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004348 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004349 return deleted;
Amith Yamasani67df64b2012-12-14 12:09:36 -08004350 }
4351
4352 @Override
4353 public Account[] getSharedAccountsAsUser(int userId) {
4354 userId = handleIncomingUser(userId);
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07004355 SQLiteDatabase db = getUserAccounts(userId).openHelper.getReadableDatabase();
4356 List<Account> accountList = AccountsDbUtils.getSharedAccounts(db);
Amith Yamasani67df64b2012-12-14 12:09:36 -08004357 Account[] accountArray = new Account[accountList.size()];
4358 accountList.toArray(accountArray);
4359 return accountArray;
4360 }
4361
4362 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004363 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004364 public Account[] getAccounts(String type, String opPackageName) {
Tejas Khorana69990d92016-08-03 11:19:40 -07004365 return getAccountsAsUser(type, UserHandle.getCallingUserId(), opPackageName);
Amith Yamasani2c7bc262012-11-05 16:46:02 -08004366 }
4367
Amith Yamasani27db4682013-03-30 17:07:47 -07004368 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004369 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004370 public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004371 int callingUid = Binder.getCallingUid();
4372 if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
4373 throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
4374 + callingUid + " with uid=" + uid);
4375 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004376 return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
4377 opPackageName);
Amith Yamasani27db4682013-03-30 17:07:47 -07004378 }
4379
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004380 @Override
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07004381 @NonNull
Svetoslavf3f02ac2015-09-08 14:36:35 -07004382 public Account[] getAccountsByTypeForPackage(String type, String packageName,
4383 String opPackageName) {
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004384 int packageUid = -1;
4385 try {
4386 packageUid = AppGlobals.getPackageManager().getPackageUid(
Jeff Sharkeycd654482016-01-08 17:42:11 -07004387 packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
4388 UserHandle.getCallingUserId());
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004389 } catch (RemoteException re) {
4390 Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
4391 return new Account[0];
4392 }
Svetoslavf3f02ac2015-09-08 14:36:35 -07004393 return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
4394 packageUid, opPackageName);
Amith Yamasani3b458ad2013-04-18 18:40:07 -07004395 }
4396
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004397 @Override
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004398 public void getAccountsByFeatures(
4399 IAccountManagerResponse response,
4400 String type,
Svetoslavf3f02ac2015-09-08 14:36:35 -07004401 String[] features,
4402 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004403 int callingUid = Binder.getCallingUid();
Fred Quintana56285a62010-12-02 14:20:51 -08004404 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4405 Log.v(TAG, "getAccounts: accountType " + type
4406 + ", response " + response
4407 + ", features " + stringArrayToString(features)
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004408 + ", caller's uid " + callingUid
Fred Quintana56285a62010-12-02 14:20:51 -08004409 + ", pid " + Binder.getCallingPid());
4410 }
Fred Quintana382601f2010-03-25 12:25:10 -07004411 if (response == null) throw new IllegalArgumentException("response is null");
4412 if (type == null) throw new IllegalArgumentException("accountType is null");
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004413 int userId = UserHandle.getCallingUserId();
4414
Svetoslavf3f02ac2015-09-08 14:36:35 -07004415 List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
4416 opPackageName);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004417 if (!visibleAccountTypes.contains(type)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004418 Bundle result = new Bundle();
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004419 // Need to return just the accounts that are from matching signatures.
Carlos Valdiviac37ee222015-06-17 20:17:37 -07004420 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
4421 try {
4422 response.onResult(result);
4423 } catch (RemoteException e) {
4424 Log.e(TAG, "Cannot respond to caller do to exception." , e);
4425 }
4426 return;
4427 }
Fred Quintana33269202009-04-20 16:05:10 -07004428 long identityToken = clearCallingIdentity();
4429 try {
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004430 UserAccounts userAccounts = getUserAccounts(userId);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004431 if (features == null || features.length == 0) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004432 Account[] accounts;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004433 synchronized (userAccounts.cacheLock) {
Amith Yamasani27db4682013-03-30 17:07:47 -07004434 accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08004435 }
Fred Quintanad4a9d6c2010-02-24 12:07:53 -08004436 Bundle result = new Bundle();
4437 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
4438 onResult(response, result);
Fred Quintanaffd0cb042009-08-15 21:45:26 -07004439 return;
4440 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00004441 new GetAccountsByTypeAndFeatureSession(
4442 userAccounts,
4443 response,
4444 type,
4445 features,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08004446 callingUid).bind();
Fred Quintana33269202009-04-20 16:05:10 -07004447 } finally {
4448 restoreCallingIdentity(identityToken);
Fred Quintana60307342009-03-24 22:48:12 -07004449 }
4450 }
4451
Fred Quintanaa698f422009-04-08 19:14:54 -07004452 private abstract class Session extends IAccountAuthenticatorResponse.Stub
Fred Quintanab839afc2009-10-14 15:57:28 -07004453 implements IBinder.DeathRecipient, ServiceConnection {
Fred Quintana60307342009-03-24 22:48:12 -07004454 IAccountManagerResponse mResponse;
4455 final String mAccountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004456 final boolean mExpectActivityLaunch;
4457 final long mCreationTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004458 final String mAccountName;
4459 // Indicates if we need to add auth details(like last credential time)
4460 final boolean mAuthDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004461 // If set, we need to update the last authenticated time. This is
4462 // currently
4463 // used on
4464 // successful confirming credentials.
4465 final boolean mUpdateLastAuthenticatedTime;
Fred Quintanaa698f422009-04-08 19:14:54 -07004466
Fred Quintana33269202009-04-20 16:05:10 -07004467 public int mNumResults = 0;
Fred Quintanaa698f422009-04-08 19:14:54 -07004468 private int mNumRequestContinued = 0;
4469 private int mNumErrors = 0;
4470
Fred Quintana60307342009-03-24 22:48:12 -07004471 IAccountAuthenticator mAuthenticator = null;
4472
Fred Quintana8570f742010-02-18 10:32:54 -08004473 private final boolean mStripAuthTokenFromResult;
Amith Yamasani04e0d262012-02-14 11:50:53 -08004474 protected final UserAccounts mAccounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004475
Amith Yamasani04e0d262012-02-14 11:50:53 -08004476 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004477 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4478 boolean authDetailsRequired) {
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004479 this(accounts, response, accountType, expectActivityLaunch, stripAuthTokenFromResult,
4480 accountName, authDetailsRequired, false /* updateLastAuthenticatedTime */);
4481 }
4482
4483 public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
4484 boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
4485 boolean authDetailsRequired, boolean updateLastAuthenticatedTime) {
Fred Quintana60307342009-03-24 22:48:12 -07004486 super();
Amith Yamasani67df64b2012-12-14 12:09:36 -08004487 //if (response == null) throw new IllegalArgumentException("response is null");
Fred Quintana33269202009-04-20 16:05:10 -07004488 if (accountType == null) throw new IllegalArgumentException("accountType is null");
Amith Yamasani04e0d262012-02-14 11:50:53 -08004489 mAccounts = accounts;
Fred Quintana8570f742010-02-18 10:32:54 -08004490 mStripAuthTokenFromResult = stripAuthTokenFromResult;
Fred Quintana60307342009-03-24 22:48:12 -07004491 mResponse = response;
4492 mAccountType = accountType;
Fred Quintanaa698f422009-04-08 19:14:54 -07004493 mExpectActivityLaunch = expectActivityLaunch;
4494 mCreationTime = SystemClock.elapsedRealtime();
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004495 mAccountName = accountName;
4496 mAuthDetailsRequired = authDetailsRequired;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004497 mUpdateLastAuthenticatedTime = updateLastAuthenticatedTime;
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004498
Fred Quintanaa698f422009-04-08 19:14:54 -07004499 synchronized (mSessions) {
4500 mSessions.put(toString(), this);
4501 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08004502 if (response != null) {
4503 try {
4504 response.asBinder().linkToDeath(this, 0 /* flags */);
4505 } catch (RemoteException e) {
4506 mResponse = null;
4507 binderDied();
4508 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004509 }
Fred Quintana60307342009-03-24 22:48:12 -07004510 }
4511
Fred Quintanaa698f422009-04-08 19:14:54 -07004512 IAccountManagerResponse getResponseAndClose() {
Fred Quintana60307342009-03-24 22:48:12 -07004513 if (mResponse == null) {
4514 // this session has already been closed
4515 return null;
4516 }
Fred Quintana60307342009-03-24 22:48:12 -07004517 IAccountManagerResponse response = mResponse;
Fred Quintanaa698f422009-04-08 19:14:54 -07004518 close(); // this clears mResponse so we need to save the response before this call
Fred Quintana60307342009-03-24 22:48:12 -07004519 return response;
4520 }
4521
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004522 /**
4523 * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
4524 * security policy.
4525 *
4526 * In particular we want to make sure that the Authenticator doesn't try to trick users
4527 * into launching aribtrary intents on the device via by tricking to click authenticator
4528 * supplied entries in the system Settings app.
4529 */
4530 protected void checkKeyIntent(
4531 int authUid,
4532 Intent intent) throws SecurityException {
4533 long bid = Binder.clearCallingIdentity();
4534 try {
4535 PackageManager pm = mContext.getPackageManager();
4536 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
4537 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
4538 int targetUid = targetActivityInfo.applicationInfo.uid;
Sandra Kwan0e961a12016-06-30 14:34:01 -07004539 if (!GrantCredentialsPermissionActivity.class.getName().equals(
4540 targetActivityInfo.getClass().getName())
4541 && !CantAddAccountActivity.class
4542 .equals(targetActivityInfo.getClass().getName())
4543 && PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
4544 targetUid)) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004545 String pkgName = targetActivityInfo.packageName;
4546 String activityName = targetActivityInfo.name;
4547 String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
4548 + "does not share a signature with the supplying authenticator (%s).";
4549 throw new SecurityException(
4550 String.format(tmpl, activityName, pkgName, mAccountType));
4551 }
4552 } finally {
4553 Binder.restoreCallingIdentity(bid);
4554 }
4555 }
4556
Fred Quintanaa698f422009-04-08 19:14:54 -07004557 private void close() {
4558 synchronized (mSessions) {
4559 if (mSessions.remove(toString()) == null) {
4560 // the session was already closed, so bail out now
4561 return;
4562 }
4563 }
4564 if (mResponse != null) {
4565 // stop listening for response deaths
4566 mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
4567
4568 // clear this so that we don't accidentally send any further results
4569 mResponse = null;
4570 }
4571 cancelTimeout();
4572 unbind();
4573 }
4574
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004575 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004576 public void binderDied() {
4577 mResponse = null;
4578 close();
4579 }
4580
4581 protected String toDebugString() {
4582 return toDebugString(SystemClock.elapsedRealtime());
4583 }
4584
4585 protected String toDebugString(long now) {
4586 return "Session: expectLaunch " + mExpectActivityLaunch
4587 + ", connected " + (mAuthenticator != null)
4588 + ", stats (" + mNumResults + "/" + mNumRequestContinued
4589 + "/" + mNumErrors + ")"
4590 + ", lifetime " + ((now - mCreationTime) / 1000.0);
4591 }
4592
Fred Quintana60307342009-03-24 22:48:12 -07004593 void bind() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004594 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4595 Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
4596 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004597 if (!bindToAuthenticator(mAccountType)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004598 Log.d(TAG, "bind attempt failed for " + toDebugString());
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004599 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
Fred Quintana60307342009-03-24 22:48:12 -07004600 }
4601 }
4602
4603 private void unbind() {
4604 if (mAuthenticator != null) {
4605 mAuthenticator = null;
Fred Quintanab839afc2009-10-14 15:57:28 -07004606 mContext.unbindService(this);
Fred Quintana60307342009-03-24 22:48:12 -07004607 }
4608 }
4609
Fred Quintana60307342009-03-24 22:48:12 -07004610 public void cancelTimeout() {
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07004611 mHandler.removeMessages(MESSAGE_TIMED_OUT, this);
Fred Quintana60307342009-03-24 22:48:12 -07004612 }
4613
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004614 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004615 public void onServiceConnected(ComponentName name, IBinder service) {
Fred Quintana60307342009-03-24 22:48:12 -07004616 mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
Fred Quintanaa698f422009-04-08 19:14:54 -07004617 try {
4618 run();
4619 } catch (RemoteException e) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004620 onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
Fred Quintanaa698f422009-04-08 19:14:54 -07004621 "remote exception");
4622 }
Fred Quintana60307342009-03-24 22:48:12 -07004623 }
4624
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004625 @Override
Fred Quintanab839afc2009-10-14 15:57:28 -07004626 public void onServiceDisconnected(ComponentName name) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004627 mAuthenticator = null;
4628 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004629 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004630 try {
4631 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4632 "disconnected");
4633 } catch (RemoteException e) {
4634 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4635 Log.v(TAG, "Session.onServiceDisconnected: "
4636 + "caught RemoteException while responding", e);
4637 }
4638 }
Fred Quintana60307342009-03-24 22:48:12 -07004639 }
4640 }
4641
Fred Quintanab839afc2009-10-14 15:57:28 -07004642 public abstract void run() throws RemoteException;
4643
Fred Quintana60307342009-03-24 22:48:12 -07004644 public void onTimedOut() {
Fred Quintanaa698f422009-04-08 19:14:54 -07004645 IAccountManagerResponse response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004646 if (response != null) {
Fred Quintana166466d2011-10-24 14:51:40 -07004647 try {
4648 response.onError(AccountManager.ERROR_CODE_REMOTE_EXCEPTION,
4649 "timeout");
4650 } catch (RemoteException e) {
4651 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4652 Log.v(TAG, "Session.onTimedOut: caught RemoteException while responding",
4653 e);
4654 }
4655 }
Fred Quintana60307342009-03-24 22:48:12 -07004656 }
4657 }
4658
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004659 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004660 public void onResult(Bundle result) {
Jeff Sharkeya04c7a72016-03-18 12:20:36 -06004661 Bundle.setDefusable(result, true);
Fred Quintanaa698f422009-04-08 19:14:54 -07004662 mNumResults++;
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004663 Intent intent = null;
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004664 if (result != null) {
4665 boolean isSuccessfulConfirmCreds = result.getBoolean(
4666 AccountManager.KEY_BOOLEAN_RESULT, false);
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004667 boolean isSuccessfulUpdateCredsOrAddAccount =
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004668 result.containsKey(AccountManager.KEY_ACCOUNT_NAME)
4669 && result.containsKey(AccountManager.KEY_ACCOUNT_TYPE);
Carlos Valdivia91979be2015-05-22 14:11:35 -07004670 // We should only update lastAuthenticated time, if
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004671 // mUpdateLastAuthenticatedTime is true and the confirmRequest
4672 // or updateRequest was successful
Carlos Valdivia91979be2015-05-22 14:11:35 -07004673 boolean needUpdate = mUpdateLastAuthenticatedTime
Simranjit Singh Kohli0b8a7c02015-06-19 12:45:27 -07004674 && (isSuccessfulConfirmCreds || isSuccessfulUpdateCredsOrAddAccount);
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004675 if (needUpdate || mAuthDetailsRequired) {
4676 boolean accountPresent = isAccountPresentForCaller(mAccountName, mAccountType);
4677 if (needUpdate && accountPresent) {
4678 updateLastAuthenticatedTime(new Account(mAccountName, mAccountType));
4679 }
4680 if (mAuthDetailsRequired) {
4681 long lastAuthenticatedTime = -1;
4682 if (accountPresent) {
4683 lastAuthenticatedTime = DatabaseUtils.longForQuery(
4684 mAccounts.openHelper.getReadableDatabase(),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004685 "SELECT " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
4686 + " FROM " +
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004687 TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
4688 + ACCOUNTS_TYPE + "=?",
4689 new String[] {
4690 mAccountName, mAccountType
4691 });
4692 }
Simranjit Singh Kohli1663b442015-04-28 11:11:12 -07004693 result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07004694 lastAuthenticatedTime);
4695 }
4696 }
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08004697 }
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004698 if (result != null
4699 && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
Carlos Valdivia6ede9c32016-03-10 20:12:32 -08004700 checkKeyIntent(
4701 Binder.getCallingUid(),
4702 intent);
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004703 }
4704 if (result != null
4705 && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) {
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004706 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAME);
4707 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004708 if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
4709 Account account = new Account(accountName, accountType);
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07004710 cancelNotification(getSigninRequiredNotificationId(mAccounts, account),
4711 new UserHandle(mAccounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07004712 }
Fred Quintana60307342009-03-24 22:48:12 -07004713 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004714 IAccountManagerResponse response;
4715 if (mExpectActivityLaunch && result != null
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004716 && result.containsKey(AccountManager.KEY_INTENT)) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004717 response = mResponse;
4718 } else {
4719 response = getResponseAndClose();
Fred Quintana60307342009-03-24 22:48:12 -07004720 }
Fred Quintana60307342009-03-24 22:48:12 -07004721 if (response != null) {
4722 try {
Fred Quintanaa698f422009-04-08 19:14:54 -07004723 if (result == null) {
Fred Quintana56285a62010-12-02 14:20:51 -08004724 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4725 Log.v(TAG, getClass().getSimpleName()
4726 + " calling onError() on response " + response);
4727 }
Fred Quintanaf7ae77c2009-10-02 17:19:31 -07004728 response.onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
Fred Quintanaa698f422009-04-08 19:14:54 -07004729 "null bundle returned");
4730 } else {
Fred Quintana8570f742010-02-18 10:32:54 -08004731 if (mStripAuthTokenFromResult) {
4732 result.remove(AccountManager.KEY_AUTHTOKEN);
4733 }
Fred Quintana56285a62010-12-02 14:20:51 -08004734 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4735 Log.v(TAG, getClass().getSimpleName()
4736 + " calling onResult() on response " + response);
4737 }
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004738 if ((result.getInt(AccountManager.KEY_ERROR_CODE, -1) > 0) &&
4739 (intent == null)) {
4740 // All AccountManager error codes are greater than 0
4741 response.onError(result.getInt(AccountManager.KEY_ERROR_CODE),
4742 result.getString(AccountManager.KEY_ERROR_MESSAGE));
4743 } else {
4744 response.onResult(result);
4745 }
Fred Quintanaa698f422009-04-08 19:14:54 -07004746 }
Fred Quintana60307342009-03-24 22:48:12 -07004747 } catch (RemoteException e) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004748 // if the caller is dead then there is no one to care about remote exceptions
4749 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4750 Log.v(TAG, "failure while notifying response", e);
4751 }
Fred Quintana60307342009-03-24 22:48:12 -07004752 }
4753 }
4754 }
Fred Quintana60307342009-03-24 22:48:12 -07004755
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004756 @Override
Fred Quintanaa698f422009-04-08 19:14:54 -07004757 public void onRequestContinued() {
4758 mNumRequestContinued++;
Fred Quintana60307342009-03-24 22:48:12 -07004759 }
4760
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08004761 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004762 public void onError(int errorCode, String errorMessage) {
Fred Quintanaa698f422009-04-08 19:14:54 -07004763 mNumErrors++;
Fred Quintanaa698f422009-04-08 19:14:54 -07004764 IAccountManagerResponse response = getResponseAndClose();
4765 if (response != null) {
4766 if (Log.isLoggable(TAG, Log.VERBOSE)) {
Fred Quintana56285a62010-12-02 14:20:51 -08004767 Log.v(TAG, getClass().getSimpleName()
4768 + " calling onError() on response " + response);
Fred Quintanaa698f422009-04-08 19:14:54 -07004769 }
4770 try {
4771 response.onError(errorCode, errorMessage);
4772 } catch (RemoteException e) {
4773 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4774 Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
4775 }
4776 }
4777 } else {
4778 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4779 Log.v(TAG, "Session.onError: already closed");
4780 }
Fred Quintana60307342009-03-24 22:48:12 -07004781 }
4782 }
Fred Quintanab839afc2009-10-14 15:57:28 -07004783
4784 /**
4785 * find the component name for the authenticator and initiate a bind
4786 * if no authenticator or the bind fails then return false, otherwise return true
4787 */
4788 private boolean bindToAuthenticator(String authenticatorType) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07004789 final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
4790 authenticatorInfo = mAuthenticatorCache.getServiceInfo(
4791 AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
Fred Quintanab839afc2009-10-14 15:57:28 -07004792 if (authenticatorInfo == null) {
4793 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4794 Log.v(TAG, "there is no authenticator for " + authenticatorType
4795 + ", bailing out");
4796 }
4797 return false;
4798 }
4799
Jeff Sharkeyce18c812016-04-27 16:00:41 -06004800 if (!isLocalUnlockedUser(mAccounts.userId)
Jeff Sharkey8a372a02016-03-16 16:25:45 -06004801 && !authenticatorInfo.componentInfo.directBootAware) {
Jeff Sharkey9d8a1042015-12-03 17:56:20 -07004802 Slog.w(TAG, "Blocking binding to authenticator " + authenticatorInfo.componentName
4803 + " which isn't encryption aware");
4804 return false;
4805 }
4806
Fred Quintanab839afc2009-10-14 15:57:28 -07004807 Intent intent = new Intent();
4808 intent.setAction(AccountManager.ACTION_AUTHENTICATOR_INTENT);
4809 intent.setComponent(authenticatorInfo.componentName);
4810 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4811 Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
4812 }
Amith Yamasani27b89e62013-01-16 12:30:11 -08004813 if (!mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE,
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004814 UserHandle.of(mAccounts.userId))) {
Fred Quintanab839afc2009-10-14 15:57:28 -07004815 if (Log.isLoggable(TAG, Log.VERBOSE)) {
4816 Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
4817 }
4818 return false;
4819 }
4820
Fred Quintanab839afc2009-10-14 15:57:28 -07004821 return true;
4822 }
Fred Quintana60307342009-03-24 22:48:12 -07004823 }
4824
Svet Ganov5d09c992016-09-07 09:57:41 -07004825 class MessageHandler extends Handler {
Fred Quintana60307342009-03-24 22:48:12 -07004826 MessageHandler(Looper looper) {
4827 super(looper);
4828 }
Costin Manolache3348f142009-09-29 18:58:36 -07004829
Carlos Valdivia5bab9da2013-09-29 05:11:56 -07004830 @Override
Fred Quintana60307342009-03-24 22:48:12 -07004831 public void handleMessage(Message msg) {
Fred Quintana60307342009-03-24 22:48:12 -07004832 switch (msg.what) {
4833 case MESSAGE_TIMED_OUT:
4834 Session session = (Session)msg.obj;
4835 session.onTimedOut();
4836 break;
4837
Amith Yamasani5be347b2013-03-31 17:44:31 -07004838 case MESSAGE_COPY_SHARED_ACCOUNT:
Esteban Talavera22dc3b72014-10-31 15:41:12 +00004839 copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
Amith Yamasani5be347b2013-03-31 17:44:31 -07004840 break;
4841
Fred Quintana60307342009-03-24 22:48:12 -07004842 default:
4843 throw new IllegalStateException("unhandled message: " + msg.what);
4844 }
4845 }
4846 }
4847
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004848 @VisibleForTesting
4849 String getPreNDatabaseName(int userId) {
Jeff Sharkey8212ae02016-02-10 14:46:43 -07004850 File systemDir = Environment.getDataSystemDirectory();
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004851 File databaseFile = new File(Environment.getUserSystemDirectory(userId),
4852 PRE_N_DATABASE_NAME);
Amith Yamasani04e0d262012-02-14 11:50:53 -08004853 if (userId == 0) {
Amith Yamasania23bb382012-04-11 15:32:07 -07004854 // Migrate old file, if it exists, to the new location.
4855 // Make sure the new file doesn't already exist. A dummy file could have been
4856 // accidentally created in the old location, causing the new one to become corrupted
4857 // as well.
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004858 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
Amith Yamasania23bb382012-04-11 15:32:07 -07004859 if (oldFile.exists() && !databaseFile.exists()) {
Marc Blankc6b0f992012-03-18 19:16:41 -07004860 // Check for use directory; create if it doesn't exist, else renameTo will fail
Amith Yamasani61f57372012-08-31 12:12:28 -07004861 File userDir = Environment.getUserSystemDirectory(userId);
Marc Blankc6b0f992012-03-18 19:16:41 -07004862 if (!userDir.exists()) {
4863 if (!userDir.mkdirs()) {
4864 throw new IllegalStateException("User dir cannot be created: " + userDir);
4865 }
4866 }
4867 if (!oldFile.renameTo(databaseFile)) {
4868 throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
4869 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004870 }
Oscar Montemayora8529f62009-11-18 10:14:20 -08004871 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08004872 return databaseFile.getPath();
Oscar Montemayora8529f62009-11-18 10:14:20 -08004873 }
4874
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004875 @VisibleForTesting
4876 String getDeDatabaseName(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004877 File databaseFile = new File(Environment.getDataSystemDeDirectory(userId),
4878 DE_DATABASE_NAME);
4879 return databaseFile.getPath();
4880 }
4881
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07004882 @VisibleForTesting
4883 String getCeDatabaseName(int userId) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07004884 File databaseFile = new File(Environment.getDataSystemCeDirectory(userId),
4885 CE_DATABASE_NAME);
4886 return databaseFile.getPath();
4887 }
4888
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004889 private static class DebugDbHelper{
4890 private DebugDbHelper() {
4891 }
4892
4893 private static String TABLE_DEBUG = "debug_table";
4894
4895 // Columns for the table
4896 private static String ACTION_TYPE = "action_type";
4897 private static String TIMESTAMP = "time";
4898 private static String CALLER_UID = "caller_uid";
4899 private static String TABLE_NAME = "table_name";
4900 private static String KEY = "primary_key";
4901
4902 // These actions correspond to the occurrence of real actions. Since
4903 // these are called by the authenticators, the uid associated will be
4904 // of the authenticator.
4905 private static String ACTION_SET_PASSWORD = "action_set_password";
4906 private static String ACTION_CLEAR_PASSWORD = "action_clear_password";
4907 private static String ACTION_ACCOUNT_ADD = "action_account_add";
4908 private static String ACTION_ACCOUNT_REMOVE = "action_account_remove";
Fyodor Kupolov35f68082016-04-06 12:14:17 -07004909 private static String ACTION_ACCOUNT_REMOVE_DE = "action_account_remove_de";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004910 private static String ACTION_AUTHENTICATOR_REMOVE = "action_authenticator_remove";
4911 private static String ACTION_ACCOUNT_RENAME = "action_account_rename";
4912
4913 // These actions don't necessarily correspond to any action on
4914 // accountDb taking place. As an example, there might be a request for
4915 // addingAccount, which might not lead to addition of account on grounds
4916 // of bad authentication. We will still be logging it to keep track of
4917 // who called.
4918 private static String ACTION_CALLED_ACCOUNT_ADD = "action_called_account_add";
4919 private static String ACTION_CALLED_ACCOUNT_REMOVE = "action_called_account_remove";
Fyodor Kupolov35f68082016-04-06 12:14:17 -07004920 private static String ACTION_SYNC_DE_CE_ACCOUNTS = "action_sync_de_ce_accounts";
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004921
Sandra Kwan920f6ef2015-11-10 14:13:29 -08004922 //This action doesn't add account to accountdb. Account is only
4923 // added in finishSession which may be in a different user profile.
Sandra Kwan78812282015-11-04 11:19:47 -08004924 private static String ACTION_CALLED_START_ACCOUNT_ADD = "action_called_start_account_add";
Sandra Kwan920f6ef2015-11-10 14:13:29 -08004925 private static String ACTION_CALLED_ACCOUNT_SESSION_FINISH =
4926 "action_called_account_session_finish";
Sandra Kwan78812282015-11-04 11:19:47 -08004927
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004928 private static SimpleDateFormat dateFromat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4929
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004930 private static void createDebugTable(SQLiteDatabase db) {
4931 db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
4932 + ACCOUNTS_ID + " INTEGER,"
4933 + ACTION_TYPE + " TEXT NOT NULL, "
4934 + TIMESTAMP + " DATETIME,"
4935 + CALLER_UID + " INTEGER NOT NULL,"
4936 + TABLE_NAME + " TEXT NOT NULL,"
4937 + KEY + " INTEGER PRIMARY KEY)");
4938 db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " (" + TIMESTAMP + ")");
4939 }
4940 }
4941
4942 private void logRecord(UserAccounts accounts, String action, String tableName) {
4943 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4944 logRecord(db, action, tableName, -1, accounts);
4945 }
4946
Simranjit Singh Kohliba0b10a2015-07-16 20:33:14 -07004947 private void logRecordWithUid(UserAccounts accounts, String action, String tableName, int uid) {
4948 SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
4949 logRecord(db, action, tableName, -1, accounts, uid);
4950 }
4951
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004952 /*
4953 * This function receives an opened writable database.
4954 */
4955 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4956 UserAccounts userAccount) {
4957 logRecord(db, action, tableName, accountId, userAccount, getCallingUid());
4958 }
4959
4960 /*
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004961 * This function receives an opened writable database and writes to it in a separate thread.
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07004962 */
4963 private void logRecord(SQLiteDatabase db, String action, String tableName, long accountId,
4964 UserAccounts userAccount, int callingUid) {
Tejas Khorana7b88f0e2016-06-13 13:06:35 -07004965
4966 class LogRecordTask implements Runnable {
4967 private final String action;
4968 private final String tableName;
4969 private final long accountId;
4970 private final UserAccounts userAccount;
4971 private final int callingUid;
4972 private final long userDebugDbInsertionPoint;
4973
4974 LogRecordTask(final String action,
4975 final String tableName,
4976 final long accountId,
4977 final UserAccounts userAccount,
4978 final int callingUid,
4979 final long userDebugDbInsertionPoint) {
4980 this.action = action;
4981 this.tableName = tableName;
4982 this.accountId = accountId;
4983 this.userAccount = userAccount;
4984 this.callingUid = callingUid;
4985 this.userDebugDbInsertionPoint = userDebugDbInsertionPoint;
4986 }
4987
4988 public void run() {
4989 SQLiteStatement logStatement = userAccount.statementForLogging;
4990 logStatement.bindLong(1, accountId);
4991 logStatement.bindString(2, action);
4992 logStatement.bindString(3, DebugDbHelper.dateFromat.format(new Date()));
4993 logStatement.bindLong(4, callingUid);
4994 logStatement.bindString(5, tableName);
4995 logStatement.bindLong(6, userDebugDbInsertionPoint);
4996 logStatement.execute();
4997 logStatement.clearBindings();
4998 }
4999 }
5000
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07005001 LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
5002 callingUid, userAccount.debugDbInsertionPoint);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005003 userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
5004 % MAX_DEBUG_DB_SIZE;
Fyodor Kupolov8873aa32016-08-25 15:25:40 -07005005 mHandler.post(logTask);
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005006 }
5007
5008 /*
5009 * This should only be called once to compile the sql statement for logging
5010 * and to find the insertion point.
5011 */
5012 private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
5013 UserAccounts userAccount) {
5014 // Initialize the count if not done earlier.
5015 int size = (int) getDebugTableRowCount(db);
5016 if (size >= MAX_DEBUG_DB_SIZE) {
5017 // Table is full, and we need to find the point where to insert.
5018 userAccount.debugDbInsertionPoint = (int) getDebugTableInsertionPoint(db);
5019 } else {
5020 userAccount.debugDbInsertionPoint = size;
5021 }
5022 compileSqlStatementForLogging(db, userAccount);
5023 }
5024
5025 private void compileSqlStatementForLogging(SQLiteDatabase db, UserAccounts userAccount) {
5026 String sql = "INSERT OR REPLACE INTO " + DebugDbHelper.TABLE_DEBUG
5027 + " VALUES (?,?,?,?,?,?)";
5028 userAccount.statementForLogging = db.compileStatement(sql);
5029 }
5030
5031 private long getDebugTableRowCount(SQLiteDatabase db) {
5032 String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + DebugDbHelper.TABLE_DEBUG;
5033 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
5034 }
5035
5036 /*
5037 * Finds the row key where the next insertion should take place. This should
5038 * be invoked only if the table has reached its full capacity.
5039 */
5040 private long getDebugTableInsertionPoint(SQLiteDatabase db) {
5041 // This query finds the smallest timestamp value (and if 2 records have
5042 // same timestamp, the choose the lower id).
5043 String queryCountDebugDbRows = new StringBuilder()
5044 .append("SELECT ").append(DebugDbHelper.KEY)
5045 .append(" FROM ").append(DebugDbHelper.TABLE_DEBUG)
5046 .append(" ORDER BY ")
5047 .append(DebugDbHelper.TIMESTAMP).append(",").append(DebugDbHelper.KEY)
5048 .append(" LIMIT 1")
5049 .toString();
5050 return DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
5051 }
5052
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005053 static class PreNDatabaseHelper extends SQLiteOpenHelper {
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005054 private final Context mContext;
5055 private final int mUserId;
5056
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005057 public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
5058 super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005059 mContext = context;
5060 mUserId = userId;
Fred Quintana60307342009-03-24 22:48:12 -07005061 }
5062
5063 @Override
5064 public void onCreate(SQLiteDatabase db) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005065 // We use PreNDatabaseHelper only if pre-N db exists
5066 throw new IllegalStateException("Legacy database cannot be created - only upgraded!");
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005067 }
5068
Amith Yamasani67df64b2012-12-14 12:09:36 -08005069 private void createSharedAccountsTable(SQLiteDatabase db) {
5070 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
5071 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5072 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5073 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5074 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5075 }
5076
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08005077 private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
5078 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
5079 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
5080 }
5081
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005082 private void addOldAccountNameColumn(SQLiteDatabase db) {
5083 db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
5084 }
5085
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005086 private void addDebugTable(SQLiteDatabase db) {
5087 DebugDbHelper.createDebugTable(db);
5088 }
5089
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005090 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
Fred Quintanaa698f422009-04-08 19:14:54 -07005091 db.execSQL(""
5092 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5093 + " BEGIN"
5094 + " DELETE FROM " + TABLE_AUTHTOKENS
5095 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5096 + " DELETE FROM " + TABLE_EXTRAS
5097 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005098 + " DELETE FROM " + TABLE_GRANTS
5099 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
Fred Quintanaa698f422009-04-08 19:14:54 -07005100 + " END");
Fred Quintana60307342009-03-24 22:48:12 -07005101 }
5102
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005103 private void createGrantsTable(SQLiteDatabase db) {
5104 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
5105 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5106 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
5107 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
5108 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
5109 + "," + GRANTS_GRANTEE_UID + "))");
5110 }
5111
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005112 private void populateMetaTableWithAuthTypeAndUID(SQLiteDatabase db,
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005113 Map<String, Integer> authTypeAndUIDMap) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005114 for (Entry<String, Integer> entry : authTypeAndUIDMap.entrySet()) {
5115 AccountsDbUtils.insertMetaAuthTypeAndUid(db, entry.getKey(), entry.getValue());
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005116 }
5117 }
5118
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005119 /**
5120 * Pre-N database may need an upgrade before splitting
5121 */
Fred Quintana60307342009-03-24 22:48:12 -07005122 @Override
5123 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Fred Quintanaa698f422009-04-08 19:14:54 -07005124 Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
Fred Quintana60307342009-03-24 22:48:12 -07005125
Fred Quintanaa698f422009-04-08 19:14:54 -07005126 if (oldVersion == 1) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005127 // no longer need to do anything since the work is done
5128 // when upgrading from version 2
5129 oldVersion++;
5130 }
5131
5132 if (oldVersion == 2) {
5133 createGrantsTable(db);
5134 db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
5135 createAccountsDeletionTrigger(db);
Fred Quintanaa698f422009-04-08 19:14:54 -07005136 oldVersion++;
5137 }
Costin Manolache3348f142009-09-29 18:58:36 -07005138
5139 if (oldVersion == 3) {
5140 db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_TYPE +
5141 " = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
5142 oldVersion++;
5143 }
Amith Yamasani67df64b2012-12-14 12:09:36 -08005144
5145 if (oldVersion == 4) {
5146 createSharedAccountsTable(db);
5147 oldVersion++;
5148 }
5149
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07005150 if (oldVersion == 5) {
5151 addOldAccountNameColumn(db);
5152 oldVersion++;
5153 }
5154
Simranjit Singh Kohli6c7c4ad2015-02-23 18:11:14 -08005155 if (oldVersion == 6) {
5156 addLastSuccessfullAuthenticatedTimeColumn(db);
5157 oldVersion++;
5158 }
5159
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005160 if (oldVersion == 7) {
5161 addDebugTable(db);
5162 oldVersion++;
5163 }
5164
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005165 if (oldVersion == 8) {
5166 populateMetaTableWithAuthTypeAndUID(
5167 db,
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005168 getAuthenticatorTypeAndUIDForUser(mContext, mUserId));
Sandra Kwan1c9026d2016-02-23 10:22:15 -08005169 oldVersion++;
5170 }
5171
Amith Yamasani67df64b2012-12-14 12:09:36 -08005172 if (oldVersion != newVersion) {
5173 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5174 }
Fred Quintana60307342009-03-24 22:48:12 -07005175 }
5176
5177 @Override
5178 public void onOpen(SQLiteDatabase db) {
5179 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
5180 }
5181 }
5182
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005183 static class DeDatabaseHelper extends SQLiteOpenHelper {
5184
5185 private final int mUserId;
5186 private volatile boolean mCeAttached;
5187
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005188 private DeDatabaseHelper(Context context, int userId, String deDatabaseName) {
5189 super(context, deDatabaseName, null, DE_DATABASE_VERSION);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005190 mUserId = userId;
5191 }
5192
5193 /**
5194 * This call needs to be made while the mCacheLock is held. The way to
5195 * ensure this is to get the lock any time a method is called ont the DatabaseHelper
5196 * @param db The database.
5197 */
5198 @Override
5199 public void onCreate(SQLiteDatabase db) {
5200 Log.i(TAG, "Creating DE database for user " + mUserId);
5201 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
5202 + ACCOUNTS_ID + " INTEGER PRIMARY KEY, "
5203 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5204 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5205 + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
5206 + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
5207 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5208
5209 db.execSQL("CREATE TABLE " + TABLE_META + " ( "
5210 + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
5211 + META_VALUE + " TEXT)");
5212
5213 createGrantsTable(db);
5214 createSharedAccountsTable(db);
5215 createAccountsDeletionTrigger(db);
5216 DebugDbHelper.createDebugTable(db);
5217 }
5218
5219 private void createSharedAccountsTable(SQLiteDatabase db) {
5220 db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
5221 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5222 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5223 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5224 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5225 }
5226
5227 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5228 db.execSQL(""
5229 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5230 + " BEGIN"
5231 + " DELETE FROM " + TABLE_GRANTS
5232 + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5233 + " END");
5234 }
5235
5236 private void createGrantsTable(SQLiteDatabase db) {
5237 db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
5238 + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5239 + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
5240 + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
5241 + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
5242 + "," + GRANTS_GRANTEE_UID + "))");
5243 }
5244
5245 @Override
5246 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5247 Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
5248
5249 if (oldVersion != newVersion) {
5250 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5251 }
5252 }
5253
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005254 public void attachCeDatabase(File ceDbFile) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005255 SQLiteDatabase db = getWritableDatabase();
5256 db.execSQL("ATTACH DATABASE '" + ceDbFile.getPath()+ "' AS ceDb");
5257 mCeAttached = true;
5258 }
5259
5260 public boolean isCeDatabaseAttached() {
5261 return mCeAttached;
5262 }
5263
5264
5265 public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
5266 if(!mCeAttached) {
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005267 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
5268 + " is still locked. CE database is not yet available.", new Throwable());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005269 }
5270 return super.getReadableDatabase();
5271 }
5272
5273 public SQLiteDatabase getWritableDatabaseUserIsUnlocked() {
5274 if(!mCeAttached) {
5275 Log.wtf(TAG, "getWritableDatabaseUserIsUnlocked called while user " + mUserId
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005276 + " is still locked. CE database is not yet available.", new Throwable());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005277 }
5278 return super.getWritableDatabase();
5279 }
5280
5281 @Override
5282 public void onOpen(SQLiteDatabase db) {
5283 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DE_DATABASE_NAME);
5284 }
5285
5286 private void migratePreNDbToDe(File preNDbFile) {
5287 Log.i(TAG, "Migrate pre-N database to DE preNDbFile=" + preNDbFile);
5288 SQLiteDatabase db = getWritableDatabase();
5289 db.execSQL("ATTACH DATABASE '" + preNDbFile.getPath() + "' AS preNDb");
5290 db.beginTransaction();
5291 // Copy accounts fields
5292 db.execSQL("INSERT INTO " + TABLE_ACCOUNTS
5293 + "(" + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
5294 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
5295 + ") "
5296 + "SELECT " + ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ", "
5297 + ACCOUNTS_PREVIOUS_NAME + ", " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
5298 + " FROM preNDb." + TABLE_ACCOUNTS);
5299 // Copy SHARED_ACCOUNTS
5300 db.execSQL("INSERT INTO " + TABLE_SHARED_ACCOUNTS
5301 + "(" + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + ") " +
5302 "SELECT " + SHARED_ACCOUNTS_ID + "," + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
5303 + " FROM preNDb." + TABLE_SHARED_ACCOUNTS);
5304 // Copy DEBUG_TABLE
5305 db.execSQL("INSERT INTO " + DebugDbHelper.TABLE_DEBUG
5306 + "(" + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
5307 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
5308 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY + ") " +
5309 "SELECT " + ACCOUNTS_ID + "," + DebugDbHelper.ACTION_TYPE + ","
5310 + DebugDbHelper.TIMESTAMP + "," + DebugDbHelper.CALLER_UID + ","
5311 + DebugDbHelper.TABLE_NAME + "," + DebugDbHelper.KEY
5312 + " FROM preNDb." + DebugDbHelper.TABLE_DEBUG);
5313 // Copy GRANTS
5314 db.execSQL("INSERT INTO " + TABLE_GRANTS
5315 + "(" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
5316 + GRANTS_GRANTEE_UID + ") " +
5317 "SELECT " + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE + ","
5318 + GRANTS_GRANTEE_UID + " FROM preNDb." + TABLE_GRANTS);
5319 // Copy META
5320 db.execSQL("INSERT INTO " + TABLE_META
5321 + "(" + META_KEY + "," + META_VALUE + ") "
5322 + "SELECT " + META_KEY + "," + META_VALUE + " FROM preNDb." + TABLE_META);
5323 db.setTransactionSuccessful();
5324 db.endTransaction();
5325
5326 db.execSQL("DETACH DATABASE preNDb");
5327 }
5328
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005329 static DeDatabaseHelper create(
5330 Context context,
5331 int userId,
5332 File preNDatabaseFile,
5333 File deDatabaseFile) {
5334 boolean newDbExists = deDatabaseFile.exists();
5335 DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
5336 deDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005337 // If the db just created, and there is a legacy db, migrate it
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005338 if (!newDbExists && preNDatabaseFile.exists()) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005339 // Migrate legacy db to the latest version - PRE_N_DATABASE_VERSION
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005340 PreNDatabaseHelper preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
5341 preNDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005342 // Open the database to force upgrade if required
5343 preNDatabaseHelper.getWritableDatabase();
5344 preNDatabaseHelper.close();
5345 // Move data without SPII to DE
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005346 deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005347 }
5348 return deDatabaseHelper;
5349 }
5350 }
5351
5352 static class CeDatabaseHelper extends SQLiteOpenHelper {
5353
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005354 public CeDatabaseHelper(Context context, String ceDatabaseName) {
5355 super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005356 }
5357
5358 /**
5359 * This call needs to be made while the mCacheLock is held.
5360 * @param db The database.
5361 */
5362 @Override
5363 public void onCreate(SQLiteDatabase db) {
5364 Log.i(TAG, "Creating CE database " + getDatabaseName());
5365 db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
5366 + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5367 + ACCOUNTS_NAME + " TEXT NOT NULL, "
5368 + ACCOUNTS_TYPE + " TEXT NOT NULL, "
5369 + ACCOUNTS_PASSWORD + " TEXT, "
5370 + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
5371
5372 db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
5373 + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5374 + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
5375 + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
5376 + AUTHTOKENS_AUTHTOKEN + " TEXT, "
5377 + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
5378
5379 db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
5380 + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
5381 + EXTRAS_ACCOUNTS_ID + " INTEGER, "
5382 + EXTRAS_KEY + " TEXT NOT NULL, "
5383 + EXTRAS_VALUE + " TEXT, "
5384 + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
5385
5386 createAccountsDeletionTrigger(db);
5387 }
5388
5389 private void createAccountsDeletionTrigger(SQLiteDatabase db) {
5390 db.execSQL(""
5391 + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
5392 + " BEGIN"
5393 + " DELETE FROM " + TABLE_AUTHTOKENS
5394 + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5395 + " DELETE FROM " + TABLE_EXTRAS
5396 + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
5397 + " END");
5398 }
5399
5400 @Override
5401 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
5402 Log.i(TAG, "Upgrade CE from version " + oldVersion + " to version " + newVersion);
5403
5404 if (oldVersion == 9) {
5405 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5406 Log.v(TAG, "onUpgrade upgrading to v10");
5407 }
5408 db.execSQL("DROP TABLE IF EXISTS " + TABLE_META);
5409 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SHARED_ACCOUNTS);
5410 // Recreate the trigger, since the old one references the table to be removed
5411 db.execSQL("DROP TRIGGER IF EXISTS " + TABLE_ACCOUNTS + "Delete");
5412 createAccountsDeletionTrigger(db);
5413 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRANTS);
5414 db.execSQL("DROP TABLE IF EXISTS " + DebugDbHelper.TABLE_DEBUG);
5415 oldVersion ++;
5416 }
5417
5418 if (oldVersion != newVersion) {
5419 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
5420 }
5421 }
5422
5423 @Override
5424 public void onOpen(SQLiteDatabase db) {
5425 if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
5426 }
5427
Fyodor Kupolov35f68082016-04-06 12:14:17 -07005428
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005429 /**
5430 * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
5431 * it also performs migration to the new CE database.
5432 * @param context
5433 * @param userId id of the user where the database is located
5434 */
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005435 static CeDatabaseHelper create(
5436 Context context,
5437 int userId,
5438 File preNDatabaseFile,
5439 File ceDatabaseFile) {
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005440 boolean newDbExists = ceDatabaseFile.exists();
5441 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5442 Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005443 + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005444 }
5445 boolean removeOldDb = false;
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005446 if (!newDbExists && preNDatabaseFile.exists()) {
5447 removeOldDb = migratePreNDbToCe(preNDatabaseFile, ceDatabaseFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005448 }
5449 // Try to open and upgrade if necessary
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005450 CeDatabaseHelper ceHelper = new CeDatabaseHelper(context, ceDatabaseFile.getPath());
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005451 ceHelper.getWritableDatabase();
5452 ceHelper.close();
5453 if (removeOldDb) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005454 Slog.i(TAG, "Migration complete - removing pre-N db " + preNDatabaseFile);
5455 if (!SQLiteDatabase.deleteDatabase(preNDatabaseFile)) {
5456 Slog.e(TAG, "Cannot remove pre-N db " + preNDatabaseFile);
5457 }
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005458 }
5459 return ceHelper;
5460 }
5461
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005462 private static boolean migratePreNDbToCe(File oldDbFile, File ceDbFile) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005463 Slog.i(TAG, "Moving pre-N DB " + oldDbFile + " to CE " + ceDbFile);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005464 try {
5465 FileUtils.copyFileOrThrow(oldDbFile, ceDbFile);
5466 } catch (IOException e) {
Fyodor Kupolov27bd37f2016-04-21 11:26:14 -07005467 Slog.e(TAG, "Cannot copy file to " + ceDbFile + " from " + oldDbFile, e);
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005468 // Try to remove potentially damaged file if I/O error occurred
5469 deleteDbFileWarnIfFailed(ceDbFile);
5470 return false;
5471 }
5472 return true;
5473 }
5474 }
5475
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005476 public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
Fred Quintana60307342009-03-24 22:48:12 -07005477 return asBinder();
5478 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005479
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005480 /**
5481 * Searches array of arguments for the specified string
5482 * @param args array of argument strings
5483 * @param value value to search for
5484 * @return true if the value is contained in the array
5485 */
5486 private static boolean scanArgs(String[] args, String value) {
5487 if (args != null) {
5488 for (String arg : args) {
5489 if (value.equals(arg)) {
5490 return true;
5491 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005492 }
5493 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005494 return false;
5495 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005496
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005497 @Override
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005498 protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
Kenny Root3abd75b2011-09-29 11:00:41 -07005499 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
5500 != PackageManager.PERMISSION_GRANTED) {
5501 fout.println("Permission Denial: can't dump AccountsManager from from pid="
5502 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
5503 + " without permission " + android.Manifest.permission.DUMP);
5504 return;
5505 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08005506 final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005507 final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
Kenny Root3abd75b2011-09-29 11:00:41 -07005508
Jeff Sharkey6eb96202012-10-10 13:13:54 -07005509 final List<UserInfo> users = getUserManager().getUsers();
5510 for (UserInfo user : users) {
5511 ipw.println("User " + user + ":");
5512 ipw.increaseIndent();
5513 dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
5514 ipw.println();
5515 ipw.decreaseIndent();
Amith Yamasani04e0d262012-02-14 11:50:53 -08005516 }
5517 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005518
Amith Yamasani04e0d262012-02-14 11:50:53 -08005519 private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
5520 String[] args, boolean isCheckinRequest) {
5521 synchronized (userAccounts.cacheLock) {
5522 final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005523
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005524 if (isCheckinRequest) {
5525 // This is a checkin request. *Only* upload the account types and the count of each.
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005526 AccountsDbUtils.dumpAccountsTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005527 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08005528 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
Amith Yamasani27db4682013-03-30 17:07:47 -07005529 Process.myUid(), null);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005530 fout.println("Accounts: " + accounts.length);
5531 for (Account account : accounts) {
5532 fout.println(" " + account);
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005533 }
Fred Quintana307da1a2010-01-21 14:24:20 -08005534
Simranjit Singh Kohli1d0c1a62015-04-09 13:58:44 -07005535 // Add debug information.
5536 fout.println();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005537 AccountsDbUtils.dumpDebugTable(db, fout);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005538 fout.println();
5539 synchronized (mSessions) {
5540 final long now = SystemClock.elapsedRealtime();
5541 fout.println("Active Sessions: " + mSessions.size());
5542 for (Session session : mSessions.values()) {
5543 fout.println(" " + session.toDebugString(now));
5544 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005545 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005546
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005547 fout.println();
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005548 mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005549 }
Jason Parks1cd7d0e2009-09-28 14:48:34 -07005550 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005551 }
5552
Amith Yamasani04e0d262012-02-14 11:50:53 -08005553 private void doNotification(UserAccounts accounts, Account account, CharSequence message,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005554 Intent intent, String packageName, final int userId) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005555 long identityToken = clearCallingIdentity();
5556 try {
5557 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5558 Log.v(TAG, "doNotification: " + message + " intent:" + intent);
5559 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005560
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005561 if (intent.getComponent() != null &&
5562 GrantCredentialsPermissionActivity.class.getName().equals(
5563 intent.getComponent().getClassName())) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005564 createNoCredentialsPermissionNotification(account, intent, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005565 } else {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005566 Context contextForUser = getContextForUser(new UserHandle(userId));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005567 final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
Fred Quintana33f889a2009-09-14 17:31:26 -07005568 intent.addCategory(String.valueOf(notificationId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005569
Fred Quintana33f889a2009-09-14 17:31:26 -07005570 final String notificationTitleFormat =
Kenny Guy07ad8dc2014-09-01 20:56:12 +01005571 contextForUser.getText(R.string.notification_title).toString();
Chris Wren1ce4b6d2015-06-11 10:19:43 -04005572 Notification n = new Notification.Builder(contextForUser)
5573 .setWhen(0)
5574 .setSmallIcon(android.R.drawable.stat_sys_warning)
5575 .setColor(contextForUser.getColor(
5576 com.android.internal.R.color.system_notification_accent_color))
5577 .setContentTitle(String.format(notificationTitleFormat, account.name))
5578 .setContentText(message)
5579 .setContentIntent(PendingIntent.getActivityAsUser(
5580 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005581 null, new UserHandle(userId)))
Chris Wren1ce4b6d2015-06-11 10:19:43 -04005582 .build();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005583 installNotification(notificationId, n, packageName, userId);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005584 }
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005585 } finally {
5586 restoreCallingIdentity(identityToken);
5587 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005588 }
5589
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005590 @VisibleForTesting
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005591 protected void installNotification(int notificationId, final Notification notification,
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005592 UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005593 installNotification(notificationId, notification, "android", user.getIdentifier());
5594 }
5595
5596 private void installNotification(int notificationId, final Notification notification,
5597 String packageName, int userId) {
5598 final long token = clearCallingIdentity();
5599 try {
5600 INotificationManager notificationManager = NotificationManager.getService();
5601 try {
5602 notificationManager.enqueueNotificationWithTag(packageName, packageName, null,
5603 notificationId, notification, new int[1], userId);
5604 } catch (RemoteException e) {
5605 /* ignore - local call */
5606 }
5607 } finally {
5608 Binder.restoreCallingIdentity(token);
5609 }
Fred Quintana56285a62010-12-02 14:20:51 -08005610 }
5611
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07005612 @VisibleForTesting
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005613 protected void cancelNotification(int id, UserHandle user) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005614 cancelNotification(id, mContext.getPackageName(), user);
5615 }
5616
5617 protected void cancelNotification(int id, String packageName, UserHandle user) {
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005618 long identityToken = clearCallingIdentity();
5619 try {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005620 INotificationManager service = INotificationManager.Stub.asInterface(
5621 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
5622 service.cancelNotificationWithTag(packageName, null, id, user.getIdentifier());
5623 } catch (RemoteException e) {
5624 /* ignore - local call */
Fred Quintana26fc5eb2009-04-09 15:05:50 -07005625 } finally {
5626 restoreCallingIdentity(identityToken);
5627 }
Fred Quintanaa698f422009-04-08 19:14:54 -07005628 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005629
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005630 private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
5631 for (String perm : permissions) {
5632 if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
5633 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5634 Log.v(TAG, " caller uid " + callingUid + " has " + perm);
5635 }
5636 final int opCode = AppOpsManager.permissionToOpCode(perm);
5637 if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp(
5638 opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) {
5639 return true;
5640 }
5641 }
5642 }
5643 return false;
5644 }
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005645
Amith Yamasani67df64b2012-12-14 12:09:36 -08005646 private int handleIncomingUser(int userId) {
5647 try {
5648 return ActivityManagerNative.getDefault().handleIncomingUser(
5649 Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
5650 } catch (RemoteException re) {
5651 // Shouldn't happen, local.
5652 }
5653 return userId;
5654 }
5655
Christopher Tateccbf84f2013-05-08 15:25:41 -07005656 private boolean isPrivileged(int callingUid) {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005657 final int callingUserId = UserHandle.getUserId(callingUid);
5658
5659 final PackageManager userPackageManager;
5660 try {
5661 userPackageManager = mContext.createPackageContextAsUser(
5662 "android", 0, new UserHandle(callingUserId)).getPackageManager();
5663 } catch (NameNotFoundException e) {
5664 return false;
5665 }
5666
5667 String[] packages = userPackageManager.getPackagesForUid(callingUid);
Fred Quintana7be59642009-08-24 18:29:25 -07005668 for (String name : packages) {
5669 try {
Jeff Sharkey6ab72d72012-10-08 16:44:37 -07005670 PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
Fred Quintana56285a62010-12-02 14:20:51 -08005671 if (packageInfo != null
Alex Klyubinb9f8a522015-02-03 11:12:59 -08005672 && (packageInfo.applicationInfo.privateFlags
5673 & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
Fred Quintana7be59642009-08-24 18:29:25 -07005674 return true;
5675 }
5676 } catch (PackageManager.NameNotFoundException e) {
5677 return false;
5678 }
5679 }
5680 return false;
5681 }
5682
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005683 private boolean permissionIsGranted(
5684 Account account, String authTokenType, int callerUid, int userId) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005685 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
5686 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5687 Log.v(TAG, "Access to " + account + " granted calling uid is system");
5688 }
5689 return true;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005690 }
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005691
5692 if (isPrivileged(callerUid)) {
5693 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5694 Log.v(TAG, "Access to " + account + " granted calling uid "
5695 + callerUid + " privileged");
5696 }
5697 return true;
5698 }
5699 if (account != null && isAccountManagedByCaller(account.type, callerUid, userId)) {
5700 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5701 Log.v(TAG, "Access to " + account + " granted calling uid "
5702 + callerUid + " manages the account");
5703 }
5704 return true;
5705 }
5706 if (account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid)) {
5707 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5708 Log.v(TAG, "Access to " + account + " granted calling uid "
5709 + callerUid + " user granted access");
5710 }
5711 return true;
5712 }
5713
5714 if (Log.isLoggable(TAG, Log.VERBOSE)) {
5715 Log.v(TAG, "Access to " + account + " not granted for uid " + callerUid);
5716 }
5717
5718 return false;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005719 }
5720
Svetoslavf3f02ac2015-09-08 14:36:35 -07005721 private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId,
5722 String opPackageName) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005723 if (accountType == null) {
5724 return false;
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005725 } else {
Svetoslavf3f02ac2015-09-08 14:36:35 -07005726 return getTypesVisibleToCaller(callingUid, userId,
5727 opPackageName).contains(accountType);
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005728 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005729 }
5730
5731 private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
5732 if (accountType == null) {
5733 return false;
5734 } else {
5735 return getTypesManagedByCaller(callingUid, userId).contains(accountType);
5736 }
5737 }
5738
Svetoslavf3f02ac2015-09-08 14:36:35 -07005739 private List<String> getTypesVisibleToCaller(int callingUid, int userId,
5740 String opPackageName) {
Ian Pedowitz358e51f2016-03-15 17:08:27 +00005741 boolean isPermitted =
5742 isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
5743 Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005744 return getTypesForCaller(callingUid, userId, isPermitted);
5745 }
5746
5747 private List<String> getTypesManagedByCaller(int callingUid, int userId) {
5748 return getTypesForCaller(callingUid, userId, false);
5749 }
5750
5751 private List<String> getTypesForCaller(
5752 int callingUid, int userId, boolean isOtherwisePermitted) {
5753 List<String> managedAccountTypes = new ArrayList<>();
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005754 long identityToken = Binder.clearCallingIdentity();
5755 Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
5756 try {
5757 serviceInfos = mAuthenticatorCache.getAllServices(userId);
5758 } finally {
5759 Binder.restoreCallingIdentity(identityToken);
5760 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005761 for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
Simranjit Singh Kohlib77d8b62015-08-07 17:07:23 -07005762 serviceInfos) {
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005763 final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
5764 if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
5765 managedAccountTypes.add(serviceInfo.type.type);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005766 }
5767 }
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005768 return managedAccountTypes;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005769 }
5770
Simranjit Singh Kohli82d01782015-04-09 17:27:06 -07005771 private boolean isAccountPresentForCaller(String accountName, String accountType) {
5772 if (getUserAccountsForCaller().accountCache.containsKey(accountType)) {
5773 for (Account account : getUserAccountsForCaller().accountCache.get(accountType)) {
5774 if (account.name.equals(accountName)) {
5775 return true;
5776 }
5777 }
5778 }
5779 return false;
5780 }
5781
Fyodor Kupolov02cb6e72015-09-18 18:20:55 -07005782 private static void checkManageUsersPermission(String message) {
5783 if (ActivityManager.checkComponentPermission(
5784 android.Manifest.permission.MANAGE_USERS, Binder.getCallingUid(), -1, true)
5785 != PackageManager.PERMISSION_GRANTED) {
5786 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
5787 }
5788 }
5789
Sudheer Shanka3b2297d2016-06-20 10:44:30 -07005790 private static void checkManageOrCreateUsersPermission(String message) {
5791 if (ActivityManager.checkComponentPermission(android.Manifest.permission.MANAGE_USERS,
5792 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED &&
5793 ActivityManager.checkComponentPermission(android.Manifest.permission.CREATE_USERS,
5794 Binder.getCallingUid(), -1, true) != PackageManager.PERMISSION_GRANTED) {
5795 throw new SecurityException("You need MANAGE_USERS or CREATE_USERS permission to: "
5796 + message);
5797 }
5798 }
5799
Amith Yamasani04e0d262012-02-14 11:50:53 -08005800 private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
5801 int callerUid) {
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005802 if (UserHandle.getAppId(callerUid) == Process.SYSTEM_UID) {
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005803 return true;
5804 }
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005805 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005806 synchronized (accounts.cacheLock) {
5807 final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005808 final String query;
5809 final String[] args;
5810
5811 if (authTokenType != null) {
5812 query = COUNT_OF_MATCHING_GRANTS;
5813 args = new String[] {String.valueOf(callerUid), authTokenType,
5814 account.name, account.type};
5815 } else {
5816 query = COUNT_OF_MATCHING_GRANTS_ANY_TOKEN;
5817 args = new String[] {String.valueOf(callerUid), account.name,
5818 account.type};
5819 }
5820 final boolean permissionGranted = DatabaseUtils.longForQuery(db, query, args) != 0;
Svet Ganov890a2102016-08-24 00:08:00 -07005821
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005822 if (!permissionGranted && ActivityManager.isRunningInTestHarness()) {
5823 // TODO: Skip this check when running automated tests. Replace this
5824 // with a more general solution.
5825 Log.d(TAG, "no credentials permission for usage of " + account + ", "
Amith Yamasani04e0d262012-02-14 11:50:53 -08005826 + authTokenType + " by uid " + callerUid
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005827 + " but ignoring since device is in test harness.");
5828 return true;
5829 }
5830 return permissionGranted;
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005831 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005832 }
5833
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005834 private boolean isSystemUid(int callingUid) {
5835 String[] packages = null;
5836 long ident = Binder.clearCallingIdentity();
5837 try {
5838 packages = mPackageManager.getPackagesForUid(callingUid);
5839 } finally {
5840 Binder.restoreCallingIdentity(ident);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005841 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005842 if (packages != null) {
5843 for (String name : packages) {
5844 try {
5845 PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
5846 if (packageInfo != null
5847 && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
5848 != 0) {
5849 return true;
5850 }
5851 } catch (PackageManager.NameNotFoundException e) {
5852 Log.w(TAG, String.format("Could not find package [%s]", name), e);
5853 }
5854 }
5855 } else {
5856 Log.w(TAG, "No known packages with uid " + callingUid);
Carlos Valdiviaffb46022015-06-08 19:07:54 -07005857 }
Carlos Valdivia6eb73a52015-06-11 13:07:11 -07005858 return false;
Carlos Valdiviadcddc472015-06-11 20:04:04 +00005859 }
5860
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005861 /** Succeeds if any of the specified permissions are granted. */
5862 private void checkReadAccountsPermitted(
5863 int callingUid,
Ian Pedowitz6cc066d2015-08-05 14:23:43 +00005864 String accountType,
Svetoslavf3f02ac2015-09-08 14:36:35 -07005865 int userId,
5866 String opPackageName) {
5867 if (!isAccountVisibleToCaller(accountType, callingUid, userId, opPackageName)) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07005868 String msg = String.format(
5869 "caller uid %s cannot access %s accounts",
5870 callingUid,
5871 accountType);
5872 Log.w(TAG, " " + msg);
5873 throw new SecurityException(msg);
5874 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005875 }
5876
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005877 private boolean canUserModifyAccounts(int userId, int callingUid) {
5878 // the managing app can always modify accounts
5879 if (isProfileOwner(callingUid)) {
5880 return true;
5881 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005882 if (getUserManager().getUserRestrictions(new UserHandle(userId))
5883 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
5884 return false;
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005885 }
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005886 return true;
5887 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005888
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005889 private boolean canUserModifyAccountsForType(int userId, String accountType, int callingUid) {
5890 // the managing app can always modify accounts
5891 if (isProfileOwner(callingUid)) {
5892 return true;
5893 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005894 DevicePolicyManager dpm = (DevicePolicyManager) mContext
5895 .getSystemService(Context.DEVICE_POLICY_SERVICE);
Alexandra Gherghina999d3942014-07-03 11:40:08 +01005896 String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
Adili Muguro4e68b652014-07-25 16:42:39 +02005897 if (typesArray == null) {
5898 return true;
5899 }
Sander Alewijnseda1350f2014-05-08 16:59:42 +01005900 for (String forbiddenType : typesArray) {
5901 if (forbiddenType.equals(accountType)) {
5902 return false;
5903 }
5904 }
Amith Yamasanie4cf7342012-12-17 11:12:09 -08005905 return true;
5906 }
5907
Benjamin Franzb6c0ce42015-11-05 10:06:51 +00005908 private boolean isProfileOwner(int uid) {
5909 final DevicePolicyManagerInternal dpmi =
5910 LocalServices.getService(DevicePolicyManagerInternal.class);
5911 return (dpmi != null)
5912 && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
5913 }
5914
Jatin Lodhia09e7e0e2013-11-07 00:14:25 -08005915 @Override
Fred Quintanad9640ec2012-05-23 12:37:00 -07005916 public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
5917 throws RemoteException {
5918 final int callingUid = getCallingUid();
5919
Svetoslav Ganov7ee37f42016-08-24 14:40:16 -07005920 if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
Fred Quintanad9640ec2012-05-23 12:37:00 -07005921 throw new SecurityException();
5922 }
5923
5924 if (value) {
5925 grantAppPermission(account, authTokenType, uid);
5926 } else {
5927 revokeAppPermission(account, authTokenType, uid);
5928 }
5929 }
5930
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005931 /**
5932 * Allow callers with the given uid permission to get credentials for account/authTokenType.
5933 * <p>
5934 * Although this is public it can only be accessed via the AccountManagerService object
5935 * which is in the system. This means we don't need to protect it with permissions.
5936 * @hide
5937 */
Svet Ganov5d09c992016-09-07 09:57:41 -07005938 void grantAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005939 if (account == null || authTokenType == null) {
5940 Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005941 return;
5942 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005943 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005944 synchronized (accounts.cacheLock) {
5945 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005946 long accountId = AccountsDbUtils.findAccountId(db, account);
5947 if (accountId >= 0) {
5948 AccountsDbUtils.insertGrant(db, accountId, authTokenType, uid);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005949 }
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005950 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07005951 UserHandle.of(accounts.userId));
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07005952
5953 cancelAccountAccessRequestNotificationIfNeeded(account, uid, true);
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005954 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005955
5956 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5957 for (AccountManagerInternal.OnAppPermissionChangeListener listener
5958 : mAppPermissionChangeListeners) {
5959 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5960 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005961 }
5962
5963 /**
5964 * Don't allow callers with the given uid permission to get credentials for
5965 * account/authTokenType.
5966 * <p>
5967 * Although this is public it can only be accessed via the AccountManagerService object
5968 * which is in the system. This means we don't need to protect it with permissions.
5969 * @hide
5970 */
Fred Quintanad9640ec2012-05-23 12:37:00 -07005971 private void revokeAppPermission(Account account, String authTokenType, int uid) {
Fred Quintana382601f2010-03-25 12:25:10 -07005972 if (account == null || authTokenType == null) {
5973 Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
Fred Quintana31957f12009-10-21 13:43:10 -07005974 return;
5975 }
Dianne Hackbornf02b60a2012-08-16 10:48:27 -07005976 UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
Amith Yamasani04e0d262012-02-14 11:50:53 -08005977 synchronized (accounts.cacheLock) {
5978 final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005979 db.beginTransaction();
5980 try {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005981 long accountId = AccountsDbUtils.findAccountId(db, account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005982 if (accountId >= 0) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07005983 AccountsDbUtils.deleteGrantsByAccountIdAuthTokenTypeAndUid(
5984 db, accountId, authTokenType, uid);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08005985 db.setTransactionSuccessful();
5986 }
5987 } finally {
5988 db.endTransaction();
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005989 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005990
Dianne Hackborn50cdf7c32012-09-23 17:08:57 -07005991 cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
5992 new UserHandle(accounts.userId));
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07005993 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07005994
5995 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
5996 for (AccountManagerInternal.OnAppPermissionChangeListener listener
5997 : mAppPermissionChangeListeners) {
5998 mHandler.post(() -> listener.onAppPermissionChanged(account, uid));
5999 }
Fred Quintanad4a1d2e2009-07-16 16:36:38 -07006000 }
Fred Quintana56285a62010-12-02 14:20:51 -08006001
6002 static final private String stringArrayToString(String[] value) {
6003 return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
6004 }
6005
Amith Yamasani04e0d262012-02-14 11:50:53 -08006006 private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
6007 final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006008 if (oldAccountsForType != null) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006009 ArrayList<Account> newAccountsList = new ArrayList<>();
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006010 for (Account curAccount : oldAccountsForType) {
6011 if (!curAccount.equals(account)) {
6012 newAccountsList.add(curAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006013 }
6014 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006015 if (newAccountsList.isEmpty()) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08006016 accounts.accountCache.remove(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006017 } else {
6018 Account[] newAccountsForType = new Account[newAccountsList.size()];
6019 newAccountsForType = newAccountsList.toArray(newAccountsForType);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006020 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006021 }
Fred Quintana56285a62010-12-02 14:20:51 -08006022 }
Amith Yamasani04e0d262012-02-14 11:50:53 -08006023 accounts.userDataCache.remove(account);
6024 accounts.authTokenCache.remove(account);
Carlos Valdiviaf193b9a2014-07-18 01:34:57 -07006025 accounts.previousNameCache.remove(account);
Fred Quintana56285a62010-12-02 14:20:51 -08006026 }
6027
6028 /**
6029 * This assumes that the caller has already checked that the account is not already present.
6030 */
Amith Yamasani04e0d262012-02-14 11:50:53 -08006031 private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
6032 Account[] accountsForType = accounts.accountCache.get(account.type);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006033 int oldLength = (accountsForType != null) ? accountsForType.length : 0;
6034 Account[] newAccountsForType = new Account[oldLength + 1];
6035 if (accountsForType != null) {
6036 System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
Fred Quintana56285a62010-12-02 14:20:51 -08006037 }
Svet Ganovf6d424f12016-09-20 20:18:53 -07006038 newAccountsForType[oldLength] = new Account(account, new AccountAccessTracker());
Amith Yamasani04e0d262012-02-14 11:50:53 -08006039 accounts.accountCache.put(account.type, newAccountsForType);
Fred Quintana56285a62010-12-02 14:20:51 -08006040 }
6041
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006042 private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
Amith Yamasani27db4682013-03-30 17:07:47 -07006043 int callingUid, String callingPackage) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006044 if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
Amith Yamasani27db4682013-03-30 17:07:47 -07006045 || callingUid == Process.myUid()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006046 return unfiltered;
6047 }
Erik Wolsheimerec1a9182016-03-17 10:39:51 -07006048 UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
Amith Yamasani0c19bf52013-10-03 10:34:58 -07006049 if (user != null && user.isRestricted()) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006050 String[] packages = mPackageManager.getPackagesForUid(callingUid);
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006051 // If any of the packages is a visible listed package, return the full set,
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006052 // otherwise return non-shared accounts only.
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006053 // This might be a temporary way to specify a visible list
6054 String visibleList = mContext.getResources().getString(
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006055 com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
6056 for (String packageName : packages) {
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006057 if (visibleList.contains(";" + packageName + ";")) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006058 return unfiltered;
6059 }
6060 }
Tejas Khorana5edff3b2016-06-28 20:59:52 -07006061 ArrayList<Account> allowed = new ArrayList<>();
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006062 Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
6063 if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006064 String requiredAccountType = "";
6065 try {
Amith Yamasanie3423092013-05-22 19:41:45 -07006066 // If there's an explicit callingPackage specified, check if that package
6067 // opted in to see restricted accounts.
6068 if (callingPackage != null) {
6069 PackageInfo pi = mPackageManager.getPackageInfo(callingPackage, 0);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006070 if (pi != null && pi.restrictedAccountType != null) {
6071 requiredAccountType = pi.restrictedAccountType;
Amith Yamasanie3423092013-05-22 19:41:45 -07006072 }
6073 } else {
6074 // Otherwise check if the callingUid has a package that has opted in
6075 for (String packageName : packages) {
6076 PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
6077 if (pi != null && pi.restrictedAccountType != null) {
6078 requiredAccountType = pi.restrictedAccountType;
Amith Yamasani27db4682013-03-30 17:07:47 -07006079 break;
6080 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006081 }
6082 }
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006083 } catch (NameNotFoundException nnfe) {
6084 }
6085 for (Account account : unfiltered) {
6086 if (account.type.equals(requiredAccountType)) {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006087 allowed.add(account);
Amith Yamasani0ac1fc92013-03-27 18:56:08 -07006088 } else {
6089 boolean found = false;
6090 for (Account shared : sharedAccounts) {
6091 if (shared.equals(account)) {
6092 found = true;
6093 break;
6094 }
6095 }
6096 if (!found) {
6097 allowed.add(account);
6098 }
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006099 }
6100 }
6101 Account[] filtered = new Account[allowed.size()];
6102 allowed.toArray(filtered);
6103 return filtered;
6104 } else {
6105 return unfiltered;
6106 }
6107 }
6108
Amith Yamasani27db4682013-03-30 17:07:47 -07006109 /*
6110 * packageName can be null. If not null, it should be used to filter out restricted accounts
6111 * that the package is not allowed to access.
6112 */
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006113 protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
Amith Yamasani27db4682013-03-30 17:07:47 -07006114 int callingUid, String callingPackage) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006115 if (accountType != null) {
Amith Yamasani04e0d262012-02-14 11:50:53 -08006116 final Account[] accounts = userAccounts.accountCache.get(accountType);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006117 if (accounts == null) {
6118 return EMPTY_ACCOUNT_ARRAY;
Fred Quintana56285a62010-12-02 14:20:51 -08006119 } else {
Amith Yamasanidf2e92a2013-03-01 17:04:38 -08006120 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
Amith Yamasani27db4682013-03-30 17:07:47 -07006121 callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08006122 }
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006123 } else {
6124 int totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006125 for (Account[] accounts : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006126 totalLength += accounts.length;
6127 }
6128 if (totalLength == 0) {
6129 return EMPTY_ACCOUNT_ARRAY;
6130 }
6131 Account[] accounts = new Account[totalLength];
6132 totalLength = 0;
Amith Yamasani04e0d262012-02-14 11:50:53 -08006133 for (Account[] accountsOfType : userAccounts.accountCache.values()) {
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006134 System.arraycopy(accountsOfType, 0, accounts, totalLength,
6135 accountsOfType.length);
6136 totalLength += accountsOfType.length;
6137 }
Amith Yamasani27db4682013-03-30 17:07:47 -07006138 return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
Fred Quintana56285a62010-12-02 14:20:51 -08006139 }
6140 }
6141
Amith Yamasani04e0d262012-02-14 11:50:53 -08006142 protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
6143 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006144 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006145 if (userDataForAccount == null) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006146 userDataForAccount = AccountsDbUtils.findUserExtrasForAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006147 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006148 }
6149 if (value == null) {
6150 userDataForAccount.remove(key);
6151 } else {
6152 userDataForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08006153 }
6154 }
6155
Carlos Valdivia91979be2015-05-22 14:11:35 -07006156 protected String readCachedTokenInternal(
6157 UserAccounts accounts,
6158 Account account,
6159 String tokenType,
6160 String callingPackage,
6161 byte[] pkgSigDigest) {
6162 synchronized (accounts.cacheLock) {
Carlos Valdiviac37ee222015-06-17 20:17:37 -07006163 return accounts.accountTokenCaches.get(
6164 account, tokenType, callingPackage, pkgSigDigest);
Carlos Valdivia91979be2015-05-22 14:11:35 -07006165 }
6166 }
6167
Amith Yamasani04e0d262012-02-14 11:50:53 -08006168 protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
6169 Account account, String key, String value) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006170 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006171 if (authTokensForAccount == null) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006172 authTokensForAccount = AccountsDbUtils.findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006173 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintanaf9f240e2011-02-24 18:27:50 -08006174 }
6175 if (value == null) {
6176 authTokensForAccount.remove(key);
6177 } else {
6178 authTokensForAccount.put(key, value);
Fred Quintana56285a62010-12-02 14:20:51 -08006179 }
6180 }
6181
Amith Yamasani04e0d262012-02-14 11:50:53 -08006182 protected String readAuthTokenInternal(UserAccounts accounts, Account account,
6183 String authTokenType) {
6184 synchronized (accounts.cacheLock) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006185 Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
Fred Quintana56285a62010-12-02 14:20:51 -08006186 if (authTokensForAccount == null) {
6187 // need to populate the cache for this account
Fyodor Kupolovef73aaa2016-03-25 10:23:42 -07006188 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006189 authTokensForAccount = AccountsDbUtils
6190 .findAuthTokensByAccount(db, account);
Amith Yamasani04e0d262012-02-14 11:50:53 -08006191 accounts.authTokenCache.put(account, authTokensForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006192 }
6193 return authTokensForAccount.get(authTokenType);
6194 }
6195 }
6196
Simranjit Kohli858511c2016-03-10 18:36:11 +00006197 protected String readUserDataInternalLocked(
6198 UserAccounts accounts, Account account, String key) {
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006199 Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00006200 if (userDataForAccount == null) {
6201 // need to populate the cache for this account
Fyodor Kupoloveeca6582016-04-08 12:14:04 -07006202 final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006203 userDataForAccount = AccountsDbUtils.findUserExtrasForAccount(db, account);
Simranjit Kohli858511c2016-03-10 18:36:11 +00006204 accounts.userDataCache.put(account, userDataForAccount);
Fred Quintana56285a62010-12-02 14:20:51 -08006205 }
Simranjit Kohli858511c2016-03-10 18:36:11 +00006206 return userDataForAccount.get(key);
Fred Quintana56285a62010-12-02 14:20:51 -08006207 }
6208
Kenny Guy07ad8dc2014-09-01 20:56:12 +01006209 private Context getContextForUser(UserHandle user) {
6210 try {
6211 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
6212 } catch (NameNotFoundException e) {
6213 // Default to mContext, not finding the package system is running as is unlikely.
6214 return mContext;
6215 }
6216 }
Sandra Kwan78812282015-11-04 11:19:47 -08006217
6218 private void sendResponse(IAccountManagerResponse response, Bundle result) {
6219 try {
6220 response.onResult(result);
6221 } catch (RemoteException e) {
6222 // if the caller is dead then there is no one to care about remote
6223 // exceptions
6224 if (Log.isLoggable(TAG, Log.VERBOSE)) {
6225 Log.v(TAG, "failure while notifying response", e);
6226 }
6227 }
6228 }
6229
6230 private void sendErrorResponse(IAccountManagerResponse response, int errorCode,
6231 String errorMessage) {
6232 try {
6233 response.onError(errorCode, errorMessage);
6234 } catch (RemoteException e) {
6235 // if the caller is dead then there is no one to care about remote
6236 // exceptions
6237 if (Log.isLoggable(TAG, Log.VERBOSE)) {
6238 Log.v(TAG, "failure while notifying response", e);
6239 }
6240 }
6241 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006242
6243 static class AccountsDbUtils {
6244
6245 static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
6246 String type) {
6247 Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
6248 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
6249 new String[]{name, type}, null, null, null);
6250 try {
6251 if (cursor.moveToNext()) {
6252 return cursor.getString(0);
6253 }
6254 return null;
6255 } finally {
6256 cursor.close();
6257 }
6258 }
6259
6260 static Map<Long, Account> findAllAccounts(SQLiteDatabase db) {
6261 LinkedHashMap<Long, Account> map = new LinkedHashMap<>();
6262 Cursor cursor = db.query(TABLE_ACCOUNTS,
6263 new String[] {ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
6264 null, null, null, null, ACCOUNTS_ID);
6265 try {
6266 while (cursor.moveToNext()) {
6267 final long accountId = cursor.getLong(0);
6268 final String accountType = cursor.getString(1);
6269 final String accountName = cursor.getString(2);
6270
6271 final Account account = new Account(accountName, accountType);
6272 map.put(accountId, account);
6273 }
6274 } finally {
6275 cursor.close();
6276 }
6277 return map;
6278 }
6279
6280 static String findAccountPreviousName(SQLiteDatabase db, Account account) {
6281 Cursor cursor = db.query(
6282 TABLE_ACCOUNTS,
6283 new String[]{ ACCOUNTS_PREVIOUS_NAME },
6284 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
6285 new String[] { account.name, account.type },
6286 null,
6287 null,
6288 null);
6289 try {
6290 if (cursor.moveToNext()) {
6291 return cursor.getString(0);
6292 }
6293 } finally {
6294 cursor.close();
6295 }
6296 return null;
6297 }
6298
6299 static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
6300 // Select accounts from CE that do not exist in DE
6301 Cursor cursor = db.rawQuery(
6302 "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
6303 + " FROM " + CE_TABLE_ACCOUNTS
6304 + " WHERE NOT EXISTS "
6305 + " (SELECT " + ACCOUNTS_ID + " FROM " + TABLE_ACCOUNTS
6306 + " WHERE " + ACCOUNTS_ID + "=" + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
6307 + " )", null);
6308 try {
6309 List<Account> accounts = new ArrayList<>(cursor.getCount());
6310 while (cursor.moveToNext()) {
6311 String accountName = cursor.getString(0);
6312 String accountType = cursor.getString(1);
6313 accounts.add(new Account(accountName, accountType));
6314 }
6315 return accounts;
6316 } finally {
6317 cursor.close();
6318 }
6319 }
6320
6321 static boolean deleteAccount(SQLiteDatabase db, long accountId) {
6322 return db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
6323 }
6324
6325 static boolean deleteCeAccount(SQLiteDatabase db, long accountId) {
6326 return db.delete(CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
6327 }
6328
6329 /**
6330 * Returns information about auth tokens and their account for the specified query parameters.
6331 * Output is in the format:
6332 * <pre><code> | AUTHTOKEN_ID | ACCOUNT_NAME | AUTH_TOKEN_TYPE |</code></pre>
6333 */
6334 static Cursor findAuthtokenForAllAccounts(SQLiteDatabase db, String accountType,
6335 String authToken) {
6336 return db.rawQuery(
6337 "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
6338 + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
6339 + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
6340 + " FROM " + CE_TABLE_ACCOUNTS
6341 + " JOIN " + CE_TABLE_AUTHTOKENS
6342 + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
6343 + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
6344 + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN
6345 + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
6346 new String[]{authToken, accountType});
6347 }
6348
6349 static boolean deleteAuthtokensByAccountIdAndType(SQLiteDatabase db, long accountId,
6350 String authtokenType) {
6351 return db.delete(CE_TABLE_AUTHTOKENS,
6352 AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
6353 new String[] {String.valueOf(accountId), authtokenType}) > 0;
6354 }
6355
6356 static boolean deleteAuthToken(SQLiteDatabase db, String authTokenId) {
6357 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "= ?",
6358 new String[] {authTokenId}) > 0;
6359 }
6360
6361 static long insertAuthToken(SQLiteDatabase db, long accountId, String authTokenType,
6362 String authToken) {
6363 ContentValues values = new ContentValues();
6364 values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
6365 values.put(AUTHTOKENS_TYPE, authTokenType);
6366 values.put(AUTHTOKENS_AUTHTOKEN, authToken);
6367 return db.insert(CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values);
6368 }
6369
6370 static Map<String, String> findAuthTokensByAccount(final SQLiteDatabase db, Account account) {
6371 HashMap<String, String> authTokensForAccount = new HashMap<>();
6372 Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
6373 COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
6374 SELECTION_AUTHTOKENS_BY_ACCOUNT,
6375 new String[]{account.name, account.type},
6376 null, null, null);
6377 try {
6378 while (cursor.moveToNext()) {
6379 final String type = cursor.getString(0);
6380 final String authToken = cursor.getString(1);
6381 authTokensForAccount.put(type, authToken);
6382 }
6383 } finally {
6384 cursor.close();
6385 }
6386 return authTokensForAccount;
6387 }
6388
6389 static int updateAccountPassword(SQLiteDatabase db, long accountId, String password) {
6390 final ContentValues values = new ContentValues();
6391 values.put(ACCOUNTS_PASSWORD, password);
6392 return db.update(CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?",
6393 new String[] {String.valueOf(accountId)});
6394 }
6395
6396 static boolean deleteAuthTokensByAccountId(SQLiteDatabase db, long accountId) {
6397 return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?",
6398 new String[] {String.valueOf(accountId)}) > 0;
6399 }
6400
6401 static long insertSharedAccount(SQLiteDatabase db, Account account) {
6402 ContentValues values = new ContentValues();
6403 values.put(ACCOUNTS_NAME, account.name);
6404 values.put(ACCOUNTS_TYPE, account.type);
6405 return db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
6406 }
6407
6408 static boolean deleteSharedAccount(SQLiteDatabase db, Account account) {
6409 return db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
6410 new String[] {account.name, account.type}) > 0;
6411 }
6412
6413 static int renameSharedAccount(SQLiteDatabase db, Account account, String newName) {
6414 final ContentValues values = new ContentValues();
6415 values.put(ACCOUNTS_NAME, newName);
6416 return db.update(TABLE_SHARED_ACCOUNTS,
6417 values,
6418 ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
6419 new String[] { account.name, account.type });
6420 }
6421
6422 static List<Account> getSharedAccounts(SQLiteDatabase db) {
6423 ArrayList<Account> accountList = new ArrayList<>();
6424 Cursor cursor = null;
6425 try {
6426 cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE},
6427 null, null, null, null, null);
6428 if (cursor != null && cursor.moveToFirst()) {
6429 int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
6430 int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
6431 do {
6432 accountList.add(new Account(cursor.getString(nameIndex),
6433 cursor.getString(typeIndex)));
6434 } while (cursor.moveToNext());
6435 }
6436 } finally {
6437 if (cursor != null) {
6438 cursor.close();
6439 }
6440 }
6441 return accountList;
6442 }
6443
6444 static long findSharedAccountId(SQLiteDatabase db, Account account) {
6445 Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_ID},
6446 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
6447 try {
6448 if (cursor.moveToNext()) {
6449 return cursor.getLong(0);
6450 }
6451 return -1;
6452 } finally {
6453 cursor.close();
6454 }
6455 }
6456
6457 static long findAccountId(SQLiteDatabase db, Account account) {
6458 Cursor cursor = db.query(
6459 TABLE_ACCOUNTS, new String[] {ACCOUNTS_ID},
6460 "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
6461 try {
6462 if (cursor.moveToNext()) {
6463 return cursor.getLong(0);
6464 }
6465 return -1;
6466 } finally {
6467 cursor.close();
6468 }
6469 }
6470
6471 static long findExtrasIdByAccountId(SQLiteDatabase db, long accountId, String key) {
6472 Cursor cursor = db.query(
6473 CE_TABLE_EXTRAS, new String[] {EXTRAS_ID},
6474 EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
6475 new String[]{key}, null, null, null);
6476 try {
6477 if (cursor.moveToNext()) {
6478 return cursor.getLong(0);
6479 }
6480 return -1;
6481 } finally {
6482 cursor.close();
6483 }
6484 }
6485
6486 static boolean updateExtra(SQLiteDatabase db, long extrasId, String value) {
6487 ContentValues values = new ContentValues();
6488 values.put(EXTRAS_VALUE, value);
6489 int rows = db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=?",
6490 new String[]{String.valueOf(extrasId)});
6491 return rows == 1;
6492 }
6493
6494 static long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
6495 ContentValues values = new ContentValues();
6496 values.put(EXTRAS_KEY, key);
6497 values.put(EXTRAS_ACCOUNTS_ID, accountId);
6498 values.put(EXTRAS_VALUE, value);
6499 return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
6500 }
6501
6502 static Map<String, String> findUserExtrasForAccount(SQLiteDatabase db, Account account) {
6503 Map<String, String> userExtrasForAccount = new HashMap<>();
6504 Cursor cursor = db.query(CE_TABLE_EXTRAS,
6505 COLUMNS_EXTRAS_KEY_AND_VALUE,
6506 SELECTION_USERDATA_BY_ACCOUNT,
6507 new String[] {account.name, account.type},
6508 null, null, null);
6509 try {
6510 while (cursor.moveToNext()) {
6511 final String tmpkey = cursor.getString(0);
6512 final String value = cursor.getString(1);
6513 userExtrasForAccount.put(tmpkey, value);
6514 }
6515 } finally {
6516 cursor.close();
6517 }
6518 return userExtrasForAccount;
6519 }
6520
6521 static long insertGrant(SQLiteDatabase db, long accountId, String authTokenType, int uid) {
6522 ContentValues values = new ContentValues();
6523 values.put(GRANTS_ACCOUNTS_ID, accountId);
6524 values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
6525 values.put(GRANTS_GRANTEE_UID, uid);
6526 return db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
6527 }
6528
6529 static boolean deleteGrantsByUid(SQLiteDatabase db, int uid) {
6530 return db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
6531 new String[] {Integer.toString(uid)}) > 0;
6532 }
6533
6534 static boolean deleteGrantsByAccountIdAuthTokenTypeAndUid(SQLiteDatabase db, long accountId, String authTokenType, long uid) {
6535 return db.delete(TABLE_GRANTS,
6536 GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
6537 + GRANTS_GRANTEE_UID + "=?",
6538 new String[] {String.valueOf(accountId), authTokenType, String.valueOf(uid)}) > 0;
6539 }
6540
6541 static List<Integer> findAllUidGrants(SQLiteDatabase db) {
6542 List<Integer> result = new ArrayList<>();
6543 final Cursor cursor = db.query(TABLE_GRANTS,
6544 new String[] {GRANTS_GRANTEE_UID},
6545 null, null, GRANTS_GRANTEE_UID, null, null);
6546 try {
6547 while (cursor.moveToNext()) {
6548 final int uid = cursor.getInt(0);
6549 result.add(uid);
6550 }
6551 } finally {
6552 cursor.close();
6553 }
6554 return result;
6555 }
6556
6557 static long findMatchingGrantsCount(SQLiteDatabase db,
6558 int uid, String authTokenType, Account account) {
6559 String[] args = { String.valueOf(uid), authTokenType,
6560 account.name, account.type};
6561 return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args);
6562 }
6563
6564 static long insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid) {
6565 ContentValues values = new ContentValues();
6566 values.put(META_KEY,
6567 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
6568 values.put(META_VALUE, uid);
6569 return db.insert(TABLE_META, null, values);
6570 }
6571
6572 static long insertOrReplaceMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType,
6573 int uid) {
6574 ContentValues values = new ContentValues();
6575 values.put(META_KEY,
6576 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
6577 values.put(META_VALUE, uid);
6578 return db.insertWithOnConflict(TABLE_META, null, values,
6579 SQLiteDatabase.CONFLICT_REPLACE);
6580 }
6581
6582
6583 static Map<String, Integer> findMetaAuthUid(SQLiteDatabase db) {
6584 Cursor metaCursor = db.query(
6585 TABLE_META,
6586 new String[] {META_KEY, META_VALUE},
6587 SELECTION_META_BY_AUTHENTICATOR_TYPE,
6588 new String[] {META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
6589 null /* groupBy */,
6590 null /* having */,
6591 META_KEY);
6592 Map<String, Integer> map = new LinkedHashMap<>();
6593 try {
6594 while (metaCursor.moveToNext()) {
6595 String type = TextUtils.split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
6596 String uidStr = metaCursor.getString(1);
6597 if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uidStr)) {
6598 // Should never happen.
6599 Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
6600 + ", uid empty: " + TextUtils.isEmpty(uidStr));
6601 continue;
6602 }
6603 int uid = Integer.parseInt(metaCursor.getString(1));
6604 map.put(type, uid);
6605 }
6606 } finally {
6607 metaCursor.close();
6608 }
6609 return map;
6610 }
6611
6612 static boolean deleteMetaByAuthTypeAndUid(SQLiteDatabase db, String type, int uid) {
6613 return db.delete(
6614 TABLE_META,
6615 META_KEY + "=? AND " + META_VALUE + "=?",
6616 new String[] {
6617 META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
6618 String.valueOf(uid)}
6619 ) > 0;
6620 }
6621
6622 static void dumpAccountsTable(SQLiteDatabase db, PrintWriter pw) {
6623 Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
6624 null, null, ACCOUNTS_TYPE, null, null);
6625 try {
6626 while (cursor.moveToNext()) {
6627 // print type,count
6628 pw.println(cursor.getString(0) + "," + cursor.getString(1));
6629 }
6630 } finally {
6631 if (cursor != null) {
6632 cursor.close();
6633 }
6634 }
6635 }
6636
6637 static void dumpDebugTable(SQLiteDatabase db, PrintWriter pw) {
6638 Cursor cursor = db.query(DebugDbHelper.TABLE_DEBUG, null,
6639 null, null, null, null, DebugDbHelper.TIMESTAMP);
6640 pw.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
6641 pw.println("Accounts History");
6642 try {
6643 while (cursor.moveToNext()) {
6644 // print type,count
6645 pw.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
6646 cursor.getString(2) + "," + cursor.getString(3) + ","
6647 + cursor.getString(4) + "," + cursor.getString(5));
6648 }
6649 } finally {
6650 cursor.close();
6651 }
6652 }
Svet Ganov890a2102016-08-24 00:08:00 -07006653 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006654
Svet Ganovf6d424f12016-09-20 20:18:53 -07006655 private final class AccountAccessTracker extends IAccountAccessTracker.Stub {
6656 @Override
6657 public void onAccountAccessed() throws RemoteException {
6658 final int uid = Binder.getCallingUid();
6659 if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
6660 return;
6661 }
6662 final int userId = UserHandle.getCallingUserId();
6663 final long identity = Binder.clearCallingIdentity();
6664 try {
6665 for (Account account : getAccounts(userId, mContext.getOpPackageName())) {
6666 IAccountAccessTracker accountTracker = account.getAccessTracker();
6667 if (accountTracker != null && asBinder() == accountTracker.asBinder()) {
6668 // An app just accessed the account. At this point it knows about
6669 // it and there is not need to hide this account from the app.
6670 if (!hasAccountAccess(account, null, uid)) {
6671 updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
6672 uid, true);
6673 }
6674 }
6675 }
6676 } finally {
6677 Binder.restoreCallingIdentity(identity);
6678 }
6679 }
6680 }
6681
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006682 private final class AccountManagerInternalImpl extends AccountManagerInternal {
Svet Ganov5d09c992016-09-07 09:57:41 -07006683 private final Object mLock = new Object();
6684
6685 @GuardedBy("mLock")
6686 private AccountManagerBackupHelper mBackupHelper;
6687
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006688 @Override
6689 public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
6690 @IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
6691 if (account == null) {
6692 Slog.w(TAG, "account cannot be null");
6693 return;
6694 }
6695 if (packageName == null) {
6696 Slog.w(TAG, "packageName cannot be null");
6697 return;
6698 }
6699 if (userId < UserHandle.USER_SYSTEM) {
6700 Slog.w(TAG, "user id must be concrete");
6701 return;
6702 }
6703 if (callback == null) {
6704 Slog.w(TAG, "callback cannot be null");
6705 return;
6706 }
6707
Svet Ganovf6d424f12016-09-20 20:18:53 -07006708 if (AccountManagerService.this.hasAccountAccess(account, packageName,
6709 new UserHandle(userId))) {
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006710 Bundle result = new Bundle();
6711 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
6712 callback.sendResult(result);
6713 return;
6714 }
6715
6716 final int uid;
6717 try {
6718 uid = mPackageManager.getPackageUidAsUser(packageName, userId);
6719 } catch (NameNotFoundException e) {
6720 Slog.e(TAG, "Unknown package " + packageName);
6721 return;
6722 }
6723
6724 Intent intent = newRequestAccountAccessIntent(account, packageName, uid, callback);
Svet Ganovf6d424f12016-09-20 20:18:53 -07006725 final UserAccounts userAccounts;
6726 synchronized (mUsers) {
6727 userAccounts = mUsers.get(userId);
6728 }
6729 doNotification(userAccounts, account, null, intent, packageName, userId);
6730 }
6731
6732 @Override
6733 public void addOnAppPermissionChangeListener(OnAppPermissionChangeListener listener) {
6734 // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
6735 mAppPermissionChangeListeners.add(listener);
6736 }
6737
6738 @Override
6739 public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) {
6740 return AccountManagerService.this.hasAccountAccess(account, null, uid);
Svetoslav Ganov5cb29732016-07-11 19:32:30 -07006741 }
Svet Ganov5d09c992016-09-07 09:57:41 -07006742
6743 @Override
6744 public byte[] backupAccountAccessPermissions(int userId) {
6745 synchronized (mLock) {
6746 if (mBackupHelper == null) {
6747 mBackupHelper = new AccountManagerBackupHelper(
6748 AccountManagerService.this, this);
6749 }
6750 return mBackupHelper.backupAccountAccessPermissions(userId);
6751 }
6752 }
6753
6754 @Override
6755 public void restoreAccountAccessPermissions(byte[] data, int userId) {
6756 synchronized (mLock) {
6757 if (mBackupHelper == null) {
6758 mBackupHelper = new AccountManagerBackupHelper(
6759 AccountManagerService.this, this);
6760 }
6761 mBackupHelper.restoreAccountAccessPermissions(data, userId);
6762 }
6763 }
Fyodor Kupolov1e8a94b2016-08-09 16:08:59 -07006764 }
Fred Quintana60307342009-03-24 22:48:12 -07006765}