blob: 5cfc49cd7edba593d7cc87cc31cb1f9623903e4c [file] [log] [blame]
Amith Yamasani52c489c2012-03-28 11:42:42 -07001/*
2 * Copyright (C) 2012 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;
Amith Yamasani52c489c2012-03-28 11:42:42 -070018
19import android.content.ContentResolver;
20import android.content.ContentValues;
21import android.content.Context;
Jim Miller158fe192013-04-17 15:23:55 -070022import android.content.pm.PackageManager;
Jim Miller187ec582013-04-15 18:27:54 -070023import android.content.pm.UserInfo;
24
25import static android.content.Context.USER_SERVICE;
Jim Miller158fe192013-04-17 15:23:55 -070026import static android.Manifest.permission.READ_PROFILE;
Amith Yamasani52c489c2012-03-28 11:42:42 -070027import android.database.Cursor;
28import android.database.sqlite.SQLiteDatabase;
29import android.database.sqlite.SQLiteOpenHelper;
Jim Millerf45bb402013-08-20 18:58:32 -070030import android.database.sqlite.SQLiteStatement;
Amith Yamasani52c489c2012-03-28 11:42:42 -070031import android.os.Binder;
Amith Yamasani61f57372012-08-31 12:12:28 -070032import android.os.Environment;
Paul Lawrence945490c2014-03-27 16:37:28 +000033import android.os.IBinder;
Amith Yamasani52c489c2012-03-28 11:42:42 -070034import android.os.RemoteException;
Paul Lawrence945490c2014-03-27 16:37:28 +000035import android.os.storage.IMountService;
36import android.os.ServiceManager;
37import android.os.storage.StorageManager;
Amith Yamasanid1645f82012-06-12 11:53:26 -070038import android.os.SystemProperties;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070039import android.os.UserHandle;
Jim Miller187ec582013-04-15 18:27:54 -070040import android.os.UserManager;
Amith Yamasani52c489c2012-03-28 11:42:42 -070041import android.provider.Settings;
42import android.provider.Settings.Secure;
Jim Miller187ec582013-04-15 18:27:54 -070043import android.provider.Settings.SettingNotFoundException;
Jim Millerde1af082013-09-11 14:58:26 -070044import android.security.KeyStore;
Amith Yamasani52c489c2012-03-28 11:42:42 -070045import android.text.TextUtils;
Jim Millerf45bb402013-08-20 18:58:32 -070046import android.util.Log;
Amith Yamasani52c489c2012-03-28 11:42:42 -070047import android.util.Slog;
48
Jeff Sharkey7a96c392012-11-15 14:01:46 -080049import com.android.internal.widget.ILockSettings;
Adrian Roos4f788452014-05-22 20:45:59 +020050import com.android.internal.widget.ILockSettingsObserver;
Jeff Sharkey7a96c392012-11-15 14:01:46 -080051import com.android.internal.widget.LockPatternUtils;
52
Amith Yamasani52c489c2012-03-28 11:42:42 -070053import java.io.File;
54import java.io.FileNotFoundException;
55import java.io.IOException;
56import java.io.RandomAccessFile;
Adrian Roos4f788452014-05-22 20:45:59 +020057import java.util.ArrayList;
Amith Yamasani52c489c2012-03-28 11:42:42 -070058import java.util.Arrays;
Jim Miller187ec582013-04-15 18:27:54 -070059import java.util.List;
Amith Yamasani52c489c2012-03-28 11:42:42 -070060
61/**
62 * Keeps the lock pattern/password data and related settings for each user.
63 * Used by LockPatternUtils. Needs to be a service because Settings app also needs
64 * to be able to save lockscreen information for secondary users.
65 * @hide
66 */
67public class LockSettingsService extends ILockSettings.Stub {
68
Jim Miller5ecd8112013-01-09 18:50:26 -080069 private static final String PERMISSION = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE";
Adrian Roos4f788452014-05-22 20:45:59 +020070
71 private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
72
Amith Yamasani52c489c2012-03-28 11:42:42 -070073 private final DatabaseHelper mOpenHelper;
74 private static final String TAG = "LockSettingsService";
75
76 private static final String TABLE = "locksettings";
77 private static final String COLUMN_KEY = "name";
78 private static final String COLUMN_USERID = "user";
79 private static final String COLUMN_VALUE = "value";
80
81 private static final String[] COLUMNS_FOR_QUERY = {
82 COLUMN_VALUE
83 };
84
85 private static final String SYSTEM_DIRECTORY = "/system/";
86 private static final String LOCK_PATTERN_FILE = "gesture.key";
87 private static final String LOCK_PASSWORD_FILE = "password.key";
88
89 private final Context mContext;
Jim Millerde1af082013-09-11 14:58:26 -070090 private LockPatternUtils mLockPatternUtils;
Paul Lawrence945490c2014-03-27 16:37:28 +000091 private boolean mFirstCallToVold;
Amith Yamasani52c489c2012-03-28 11:42:42 -070092
Adrian Roos4f788452014-05-22 20:45:59 +020093 private final ArrayList<LockSettingsObserver> mObservers = new ArrayList<>();
94
Amith Yamasani52c489c2012-03-28 11:42:42 -070095 public LockSettingsService(Context context) {
96 mContext = context;
97 // Open the database
98 mOpenHelper = new DatabaseHelper(mContext);
Jim Millerde1af082013-09-11 14:58:26 -070099
100 mLockPatternUtils = new LockPatternUtils(context);
Paul Lawrence945490c2014-03-27 16:37:28 +0000101 mFirstCallToVold = true;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700102 }
103
104 public void systemReady() {
105 migrateOldData();
106 }
107
108 private void migrateOldData() {
109 try {
Jim Miller187ec582013-04-15 18:27:54 -0700110 // These Settings moved before multi-user was enabled, so we only have to do it for the
111 // root user.
112 if (getString("migrated", null, 0) == null) {
113 final ContentResolver cr = mContext.getContentResolver();
114 for (String validSetting : VALID_SETTINGS) {
115 String value = Settings.Secure.getString(cr, validSetting);
116 if (value != null) {
117 setString(validSetting, value, 0);
118 }
119 }
120 // No need to move the password / pattern files. They're already in the right place.
121 setString("migrated", "true", 0);
122 Slog.i(TAG, "Migrated lock settings to new location");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700123 }
124
Jim Miller187ec582013-04-15 18:27:54 -0700125 // These Settings changed after multi-user was enabled, hence need to be moved per user.
126 if (getString("migrated_user_specific", null, 0) == null) {
127 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
128 final ContentResolver cr = mContext.getContentResolver();
129 List<UserInfo> users = um.getUsers();
130 for (int user = 0; user < users.size(); user++) {
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700131 // Migrate owner info
132 final int userId = users.get(user).id;
133 final String OWNER_INFO = Secure.LOCK_SCREEN_OWNER_INFO;
134 String ownerInfo = Settings.Secure.getStringForUser(cr, OWNER_INFO, userId);
135 if (ownerInfo != null) {
136 setString(OWNER_INFO, ownerInfo, userId);
137 Settings.Secure.putStringForUser(cr, ownerInfo, "", userId);
138 }
Jim Miller187ec582013-04-15 18:27:54 -0700139
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700140 // Migrate owner info enabled. Note there was a bug where older platforms only
141 // stored this value if the checkbox was toggled at least once. The code detects
142 // this case by handling the exception.
143 final String OWNER_INFO_ENABLED = Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
144 boolean enabled;
145 try {
146 int ivalue = Settings.Secure.getIntForUser(cr, OWNER_INFO_ENABLED, userId);
147 enabled = ivalue != 0;
148 setLong(OWNER_INFO_ENABLED, enabled ? 1 : 0, userId);
149 } catch (SettingNotFoundException e) {
150 // Setting was never stored. Store it if the string is not empty.
151 if (!TextUtils.isEmpty(ownerInfo)) {
152 setLong(OWNER_INFO_ENABLED, 1, userId);
Jim Miller187ec582013-04-15 18:27:54 -0700153 }
154 }
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700155 Settings.Secure.putIntForUser(cr, OWNER_INFO_ENABLED, 0, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700156 }
Jim Miller187ec582013-04-15 18:27:54 -0700157 // No need to move the password / pattern files. They're already in the right place.
158 setString("migrated_user_specific", "true", 0);
159 Slog.i(TAG, "Migrated per-user lock settings to new location");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700160 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700161 } catch (RemoteException re) {
Jim Miller187ec582013-04-15 18:27:54 -0700162 Slog.e(TAG, "Unable to migrate old data", re);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700163 }
164 }
165
Jim Miller5ecd8112013-01-09 18:50:26 -0800166 private final void checkWritePermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800167 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700168 }
169
Jim Miller5ecd8112013-01-09 18:50:26 -0800170 private final void checkPasswordReadPermission(int userId) {
Jim Miller505329b2013-11-08 13:25:36 -0800171 mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
Amith Yamasani52c489c2012-03-28 11:42:42 -0700172 }
173
Jim Miller158fe192013-04-17 15:23:55 -0700174 private final void checkReadPermission(String requestedKey, int userId) {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700175 final int callingUid = Binder.getCallingUid();
Jim Miller158fe192013-04-17 15:23:55 -0700176 for (int i = 0; i < READ_PROFILE_PROTECTED_SETTINGS.length; i++) {
177 String key = READ_PROFILE_PROTECTED_SETTINGS[i];
178 if (key.equals(requestedKey) && mContext.checkCallingOrSelfPermission(READ_PROFILE)
179 != PackageManager.PERMISSION_GRANTED) {
180 throw new SecurityException("uid=" + callingUid
181 + " needs permission " + READ_PROFILE + " to read "
182 + requestedKey + " for user " + userId);
183 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700184 }
185 }
186
187 @Override
188 public void setBoolean(String key, boolean value, int userId) throws RemoteException {
189 checkWritePermission(userId);
190
191 writeToDb(key, value ? "1" : "0", userId);
192 }
193
194 @Override
195 public void setLong(String key, long value, int userId) throws RemoteException {
196 checkWritePermission(userId);
197
198 writeToDb(key, Long.toString(value), userId);
199 }
200
201 @Override
202 public void setString(String key, String value, int userId) throws RemoteException {
203 checkWritePermission(userId);
204
205 writeToDb(key, value, userId);
206 }
207
208 @Override
209 public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700210 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700211
212 String value = readFromDb(key, null, userId);
213 return TextUtils.isEmpty(value) ?
214 defaultValue : (value.equals("1") || value.equals("true"));
215 }
216
217 @Override
218 public long getLong(String key, long defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700219 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700220
221 String value = readFromDb(key, null, userId);
222 return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
223 }
224
225 @Override
226 public String getString(String key, String defaultValue, int userId) throws RemoteException {
Jim Miller158fe192013-04-17 15:23:55 -0700227 checkReadPermission(key, userId);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700228
229 return readFromDb(key, defaultValue, userId);
230 }
231
Adrian Roos4f788452014-05-22 20:45:59 +0200232 @Override
233 public void registerObserver(ILockSettingsObserver remote) throws RemoteException {
234 synchronized (mObservers) {
235 for (int i = 0; i < mObservers.size(); i++) {
236 if (mObservers.get(i).remote.asBinder() == remote.asBinder()) {
237 boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
238 if (isDebuggable) {
239 throw new IllegalStateException("Observer was already registered.");
240 } else {
241 Log.e(TAG, "Observer was already registered.");
242 return;
243 }
244 }
245 }
246 LockSettingsObserver o = new LockSettingsObserver();
247 o.remote = remote;
248 o.remote.asBinder().linkToDeath(o, 0);
249 mObservers.add(o);
250 }
251 }
252
253 @Override
254 public void unregisterObserver(ILockSettingsObserver remote) throws RemoteException {
255 synchronized (mObservers) {
256 for (int i = 0; i < mObservers.size(); i++) {
257 if (mObservers.get(i).remote.asBinder() == remote.asBinder()) {
258 mObservers.remove(i);
259 return;
260 }
261 }
262 }
263 }
264
265 public void notifyObservers(String key, int userId) {
266 synchronized (mObservers) {
267 for (int i = 0; i < mObservers.size(); i++) {
268 try {
269 mObservers.get(i).remote.onLockSettingChanged(key, userId);
270 } catch (RemoteException e) {
271 // The stack trace is not really helpful here.
272 Log.e(TAG, "Failed to notify ILockSettingsObserver: " + e);
273 }
274 }
275 }
276 }
277
Amith Yamasani52c489c2012-03-28 11:42:42 -0700278 private String getLockPatternFilename(int userId) {
279 String dataSystemDirectory =
280 android.os.Environment.getDataDirectory().getAbsolutePath() +
281 SYSTEM_DIRECTORY;
282 if (userId == 0) {
283 // Leave it in the same place for user 0
284 return dataSystemDirectory + LOCK_PATTERN_FILE;
285 } else {
Amith Yamasani61f57372012-08-31 12:12:28 -0700286 return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE)
287 .getAbsolutePath();
Amith Yamasani52c489c2012-03-28 11:42:42 -0700288 }
289 }
290
291 private String getLockPasswordFilename(int userId) {
292 String dataSystemDirectory =
293 android.os.Environment.getDataDirectory().getAbsolutePath() +
294 SYSTEM_DIRECTORY;
295 if (userId == 0) {
296 // Leave it in the same place for user 0
297 return dataSystemDirectory + LOCK_PASSWORD_FILE;
298 } else {
Amith Yamasani61f57372012-08-31 12:12:28 -0700299 return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
300 .getAbsolutePath();
Amith Yamasani52c489c2012-03-28 11:42:42 -0700301 }
302 }
303
304 @Override
305 public boolean havePassword(int userId) throws RemoteException {
306 // Do we need a permissions check here?
307
308 return new File(getLockPasswordFilename(userId)).length() > 0;
309 }
310
311 @Override
312 public boolean havePattern(int userId) throws RemoteException {
313 // Do we need a permissions check here?
314
315 return new File(getLockPatternFilename(userId)).length() > 0;
316 }
317
Jim Millerde1af082013-09-11 14:58:26 -0700318 private void maybeUpdateKeystore(String password, int userId) {
319 if (userId == UserHandle.USER_OWNER) {
320 final KeyStore keyStore = KeyStore.getInstance();
321 // Conditionally reset the keystore if empty. If non-empty, we are just
322 // switching key guard type
323 if (TextUtils.isEmpty(password) && keyStore.isEmpty()) {
324 keyStore.reset();
325 } else {
326 // Update the keystore password
327 keyStore.password(password);
328 }
329 }
330 }
331
Amith Yamasani52c489c2012-03-28 11:42:42 -0700332 @Override
Jim Millerde1af082013-09-11 14:58:26 -0700333 public void setLockPattern(String pattern, int userId) throws RemoteException {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700334 checkWritePermission(userId);
335
Jim Millerde1af082013-09-11 14:58:26 -0700336 maybeUpdateKeystore(pattern, userId);
337
338 final byte[] hash = LockPatternUtils.patternToHash(
339 LockPatternUtils.stringToPattern(pattern));
Amith Yamasani52c489c2012-03-28 11:42:42 -0700340 writeFile(getLockPatternFilename(userId), hash);
341 }
342
343 @Override
Jim Millerde1af082013-09-11 14:58:26 -0700344 public void setLockPassword(String password, int userId) throws RemoteException {
345 checkWritePermission(userId);
346
347 maybeUpdateKeystore(password, userId);
348
349 writeFile(getLockPasswordFilename(userId), mLockPatternUtils.passwordToHash(password));
350 }
351
352 @Override
353 public boolean checkPattern(String pattern, int userId) throws RemoteException {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700354 checkPasswordReadPermission(userId);
355 try {
356 // Read all the bytes from the file
357 RandomAccessFile raf = new RandomAccessFile(getLockPatternFilename(userId), "r");
358 final byte[] stored = new byte[(int) raf.length()];
359 int got = raf.read(stored, 0, stored.length);
360 raf.close();
361 if (got <= 0) {
362 return true;
363 }
364 // Compare the hash from the file with the entered pattern's hash
Jim Millerde1af082013-09-11 14:58:26 -0700365 final byte[] hash = LockPatternUtils.patternToHash(
366 LockPatternUtils.stringToPattern(pattern));
367 final boolean matched = Arrays.equals(stored, hash);
368 if (matched && !TextUtils.isEmpty(pattern)) {
369 maybeUpdateKeystore(pattern, userId);
370 }
371 return matched;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700372 } catch (FileNotFoundException fnfe) {
373 Slog.e(TAG, "Cannot read file " + fnfe);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700374 } catch (IOException ioe) {
375 Slog.e(TAG, "Cannot read file " + ioe);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700376 }
Jim Millerde1af082013-09-11 14:58:26 -0700377 return true;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700378 }
379
380 @Override
Jim Millerde1af082013-09-11 14:58:26 -0700381 public boolean checkPassword(String password, int userId) throws RemoteException {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700382 checkPasswordReadPermission(userId);
383
384 try {
385 // Read all the bytes from the file
386 RandomAccessFile raf = new RandomAccessFile(getLockPasswordFilename(userId), "r");
387 final byte[] stored = new byte[(int) raf.length()];
388 int got = raf.read(stored, 0, stored.length);
389 raf.close();
390 if (got <= 0) {
391 return true;
392 }
393 // Compare the hash from the file with the entered password's hash
Jim Millerde1af082013-09-11 14:58:26 -0700394 final byte[] hash = mLockPatternUtils.passwordToHash(password);
395 final boolean matched = Arrays.equals(stored, hash);
396 if (matched && !TextUtils.isEmpty(password)) {
397 maybeUpdateKeystore(password, userId);
398 }
399 return matched;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700400 } catch (FileNotFoundException fnfe) {
401 Slog.e(TAG, "Cannot read file " + fnfe);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700402 } catch (IOException ioe) {
403 Slog.e(TAG, "Cannot read file " + ioe);
Amith Yamasani52c489c2012-03-28 11:42:42 -0700404 }
Jim Millerde1af082013-09-11 14:58:26 -0700405 return true;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700406 }
407
408 @Override
Paul Lawrence945490c2014-03-27 16:37:28 +0000409 public boolean checkVoldPassword(int userId) throws RemoteException {
410 if (!mFirstCallToVold) {
411 return false;
412 }
413 mFirstCallToVold = false;
414
415 checkPasswordReadPermission(userId);
416
417 // There's no guarantee that this will safely connect, but if it fails
418 // we will simply show the lock screen when we shouldn't, so relatively
419 // benign. There is an outside chance something nasty would happen if
420 // this service restarted before vold stales out the password in this
421 // case. The nastiness is limited to not showing the lock screen when
422 // we should, within the first minute of decrypting the phone if this
423 // service can't connect to vold, it restarts, and then the new instance
424 // does successfully connect.
425 final IMountService service = getMountService();
426 String password = service.getPassword();
427 service.clearPassword();
428 if (password == null) {
429 return false;
430 }
431
432 try {
433 if (mLockPatternUtils.isLockPatternEnabled()) {
434 if (checkPattern(password, userId)) {
435 return true;
436 }
437 }
438 } catch (Exception e) {
439 }
440
441 try {
442 if (mLockPatternUtils.isLockPasswordEnabled()) {
443 if (checkPassword(password, userId)) {
444 return true;
445 }
446 }
447 } catch (Exception e) {
448 }
449
450 return false;
451 }
452
453 @Override
Amith Yamasani52c489c2012-03-28 11:42:42 -0700454 public void removeUser(int userId) {
455 checkWritePermission(userId);
456
457 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
458 try {
459 File file = new File(getLockPasswordFilename(userId));
460 if (file.exists()) {
461 file.delete();
462 }
463 file = new File(getLockPatternFilename(userId));
464 if (file.exists()) {
465 file.delete();
466 }
467
468 db.beginTransaction();
469 db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null);
470 db.setTransactionSuccessful();
471 } finally {
472 db.endTransaction();
473 }
474 }
475
476 private void writeFile(String name, byte[] hash) {
477 try {
478 // Write the hash to file
479 RandomAccessFile raf = new RandomAccessFile(name, "rw");
480 // Truncate the file if pattern is null, to clear the lock
481 if (hash == null || hash.length == 0) {
482 raf.setLength(0);
483 } else {
484 raf.write(hash, 0, hash.length);
485 }
486 raf.close();
487 } catch (IOException ioe) {
488 Slog.e(TAG, "Error writing to file " + ioe);
489 }
490 }
491
492 private void writeToDb(String key, String value, int userId) {
Amith Yamasanid1645f82012-06-12 11:53:26 -0700493 writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId);
Adrian Roos4f788452014-05-22 20:45:59 +0200494 notifyObservers(key, userId);
Amith Yamasanid1645f82012-06-12 11:53:26 -0700495 }
496
497 private void writeToDb(SQLiteDatabase db, String key, String value, int userId) {
Amith Yamasani52c489c2012-03-28 11:42:42 -0700498 ContentValues cv = new ContentValues();
499 cv.put(COLUMN_KEY, key);
500 cv.put(COLUMN_USERID, userId);
501 cv.put(COLUMN_VALUE, value);
502
Amith Yamasani52c489c2012-03-28 11:42:42 -0700503 db.beginTransaction();
504 try {
505 db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?",
506 new String[] {key, Integer.toString(userId)});
507 db.insert(TABLE, null, cv);
508 db.setTransactionSuccessful();
509 } finally {
510 db.endTransaction();
511 }
512 }
513
514 private String readFromDb(String key, String defaultValue, int userId) {
515 Cursor cursor;
516 String result = defaultValue;
517 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
518 if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY,
519 COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?",
520 new String[] { Integer.toString(userId), key },
521 null, null, null)) != null) {
522 if (cursor.moveToFirst()) {
523 result = cursor.getString(0);
524 }
525 cursor.close();
526 }
527 return result;
528 }
529
530 class DatabaseHelper extends SQLiteOpenHelper {
531 private static final String TAG = "LockSettingsDB";
532 private static final String DATABASE_NAME = "locksettings.db";
533
Jim Millerf45bb402013-08-20 18:58:32 -0700534 private static final int DATABASE_VERSION = 2;
Amith Yamasani52c489c2012-03-28 11:42:42 -0700535
536 public DatabaseHelper(Context context) {
537 super(context, DATABASE_NAME, null, DATABASE_VERSION);
538 setWriteAheadLoggingEnabled(true);
539 }
540
541 private void createTable(SQLiteDatabase db) {
542 db.execSQL("CREATE TABLE " + TABLE + " (" +
543 "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
544 COLUMN_KEY + " TEXT," +
545 COLUMN_USERID + " INTEGER," +
546 COLUMN_VALUE + " TEXT" +
547 ");");
548 }
549
550 @Override
551 public void onCreate(SQLiteDatabase db) {
552 createTable(db);
Amith Yamasanid1645f82012-06-12 11:53:26 -0700553 initializeDefaults(db);
554 }
555
556 private void initializeDefaults(SQLiteDatabase db) {
557 // Get the lockscreen default from a system property, if available
558 boolean lockScreenDisable = SystemProperties.getBoolean("ro.lockscreen.disable.default",
559 false);
560 if (lockScreenDisable) {
561 writeToDb(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
562 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700563 }
564
565 @Override
566 public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
Jim Millerf45bb402013-08-20 18:58:32 -0700567 int upgradeVersion = oldVersion;
568 if (upgradeVersion == 1) {
569 // Set the initial value for {@link LockPatternUtils#LOCKSCREEN_WIDGETS_ENABLED}
570 // during upgrade based on whether each user previously had widgets in keyguard.
571 maybeEnableWidgetSettingForUsers(db);
572 upgradeVersion = 2;
573 }
574
575 if (upgradeVersion != DATABASE_VERSION) {
576 Log.w(TAG, "Failed to upgrade database!");
577 }
578 }
579
580 private void maybeEnableWidgetSettingForUsers(SQLiteDatabase db) {
581 final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
582 final ContentResolver cr = mContext.getContentResolver();
Jim Millerf45bb402013-08-20 18:58:32 -0700583 final List<UserInfo> users = um.getUsers();
584 for (int i = 0; i < users.size(); i++) {
585 final int userId = users.get(i).id;
Jim Millerde1af082013-09-11 14:58:26 -0700586 final boolean enabled = mLockPatternUtils.hasWidgetsEnabledInKeyguard(userId);
Jim Millerf45bb402013-08-20 18:58:32 -0700587 Log.v(TAG, "Widget upgrade uid=" + userId + ", enabled="
Jim Millerde1af082013-09-11 14:58:26 -0700588 + enabled + ", w[]=" + mLockPatternUtils.getAppWidgets());
Jim Millerf45bb402013-08-20 18:58:32 -0700589 loadSetting(db, LockPatternUtils.LOCKSCREEN_WIDGETS_ENABLED, userId, enabled);
590 }
591 }
592
593 private void loadSetting(SQLiteDatabase db, String key, int userId, boolean value) {
594 SQLiteStatement stmt = null;
595 try {
596 stmt = db.compileStatement(
597 "INSERT OR REPLACE INTO locksettings(name,user,value) VALUES(?,?,?);");
598 stmt.bindString(1, key);
599 stmt.bindLong(2, userId);
600 stmt.bindLong(3, value ? 1 : 0);
601 stmt.execute();
602 } finally {
603 if (stmt != null) stmt.close();
604 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700605 }
606 }
607
608 private static final String[] VALID_SETTINGS = new String[] {
609 LockPatternUtils.LOCKOUT_PERMANENT_KEY,
610 LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
611 LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
612 LockPatternUtils.PASSWORD_TYPE_KEY,
613 LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
614 LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
615 LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
616 LockPatternUtils.LOCKSCREEN_OPTIONS,
617 LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
618 LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
619 LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
620 LockPatternUtils.PASSWORD_HISTORY_KEY,
621 Secure.LOCK_PATTERN_ENABLED,
622 Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
623 Secure.LOCK_PATTERN_VISIBLE,
624 Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
Jim Miller187ec582013-04-15 18:27:54 -0700625 };
626
Jim Miller2d8ecf9d2013-04-22 17:17:03 -0700627 // These are protected with a read permission
628 private static final String[] READ_PROFILE_PROTECTED_SETTINGS = new String[] {
Jim Miller187ec582013-04-15 18:27:54 -0700629 Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
630 Secure.LOCK_SCREEN_OWNER_INFO
631 };
Paul Lawrence945490c2014-03-27 16:37:28 +0000632
633 private IMountService getMountService() {
634 final IBinder service = ServiceManager.getService("mount");
635 if (service != null) {
636 return IMountService.Stub.asInterface(service);
637 }
638 return null;
639 }
Adrian Roos4f788452014-05-22 20:45:59 +0200640
641 private class LockSettingsObserver implements DeathRecipient {
642 ILockSettingsObserver remote;
643
644 @Override
645 public void binderDied() {
646 mObservers.remove(this);
647 }
648 }
Amith Yamasani52c489c2012-03-28 11:42:42 -0700649}