summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--core/java/android/provider/Settings.java28
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl34
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java224
-rw-r--r--core/java/com/android/internal/widget/LockSettingsService.java388
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java23
-rw-r--r--policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java27
-rw-r--r--policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java6
-rw-r--r--services/java/com/android/server/SystemServer.java15
9 files changed, 629 insertions, 117 deletions
diff --git a/Android.mk b/Android.mk
index f65f9e4133b5..d125e6b0a9d9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -171,6 +171,7 @@ LOCAL_SRC_FILES += \
core/java/com/android/internal/view/IInputMethodClient.aidl \
core/java/com/android/internal/view/IInputMethodManager.aidl \
core/java/com/android/internal/view/IInputMethodSession.aidl \
+ core/java/com/android/internal/widget/ILockSettings.aidl \
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
keystore/java/android/security/IKeyChainAliasCallback.aidl \
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 830a85fb2b3e..371e2a1bcc01 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -36,14 +36,19 @@ import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.BatteryManager;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserId;
import android.speech.tts.TextToSpeech;
import android.text.TextUtils;
import android.util.AndroidException;
import android.util.Log;
import android.view.WindowOrientationListener;
+import com.android.internal.widget.ILockSettings;
+
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.HashSet;
@@ -2253,6 +2258,16 @@ public final class Settings {
// Populated lazily, guarded by class object:
private static NameValueCache sNameValueCache = null;
+ private static ILockSettings sLockSettings = null;
+
+ private static final HashSet<String> MOVED_TO_LOCK_SETTINGS;
+ static {
+ MOVED_TO_LOCK_SETTINGS = new HashSet<String>(3);
+ MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_ENABLED);
+ MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_VISIBLE);
+ MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
+ }
+
/**
* Look up a name in the database.
* @param resolver to access the database with
@@ -2264,6 +2279,19 @@ public final class Settings {
sNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI,
CALL_METHOD_GET_SECURE);
}
+
+ if (sLockSettings == null) {
+ sLockSettings = ILockSettings.Stub.asInterface(
+ (IBinder) ServiceManager.getService("lock_settings"));
+ }
+ if (sLockSettings != null && MOVED_TO_LOCK_SETTINGS.contains(name)) {
+ try {
+ return sLockSettings.getString(name, "0", UserId.getCallingUserId());
+ } catch (RemoteException re) {
+ // Fall through
+ }
+ }
+
return sNameValueCache.getString(resolver, name);
}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
new file mode 100644
index 000000000000..c72c77097412
--- /dev/null
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+/** {@hide} */
+interface ILockSettings {
+ void setBoolean(in String key, in boolean value, in int userId);
+ void setLong(in String key, in long value, in int userId);
+ void setString(in String key, in String value, in int userId);
+ boolean getBoolean(in String key, in boolean defaultValue, in int userId);
+ long getLong(in String key, in long defaultValue, in int userId);
+ String getString(in String key, in String defaultValue, in int userId);
+ void setLockPattern(in byte[] hash, int userId);
+ boolean checkPattern(in byte[] hash, int userId);
+ void setLockPassword(in byte[] hash, int userId);
+ boolean checkPassword(in byte[] hash, int userId);
+ boolean havePattern(int userId);
+ boolean havePassword(int userId);
+ void removeUser(int userId);
+}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 93f90f6fa175..4d308dd98564 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -21,15 +21,20 @@ import com.android.internal.telephony.ITelephony;
import com.google.android.collect.Lists;
import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.FileObserver;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserId;
import android.os.storage.IMountService;
import android.provider.Settings;
import android.security.KeyStore;
@@ -59,10 +64,6 @@ public class LockPatternUtils {
private static final String TAG = "LockPatternUtils";
- private static final String SYSTEM_DIRECTORY = "/system/";
- private static final String LOCK_PATTERN_FILE = "gesture.key";
- private static final String LOCK_PASSWORD_FILE = "password.key";
-
/**
* The maximum number of incorrect attempts before the user is prevented
* from trying again for {@link #FAILED_ATTEMPT_TIMEOUT_MS}.
@@ -111,14 +112,14 @@ public class LockPatternUtils {
*/
public static final int FLAG_BIOMETRIC_WEAK_LIVELINESS = 0x1;
- private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
- private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
- private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
+ protected final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
+ protected final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
+ protected final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
- private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
- private final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled";
- private final static String LOCKSCREEN_OPTIONS = "lockscreen.options";
+ protected final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt";
+ protected final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled";
+ protected final static String LOCKSCREEN_OPTIONS = "lockscreen.options";
public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK
= "lockscreen.biometric_weak_fallback";
public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY
@@ -126,35 +127,13 @@ public class LockPatternUtils {
public final static String LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS
= "lockscreen.power_button_instantly_locks";
- private final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
+ protected final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory";
private final Context mContext;
private final ContentResolver mContentResolver;
private DevicePolicyManager mDevicePolicyManager;
- private static String sLockPatternFilename;
- private static String sLockPasswordFilename;
-
- private static final AtomicBoolean sHaveNonZeroPatternFile = new AtomicBoolean(false);
- private static final AtomicBoolean sHaveNonZeroPasswordFile = new AtomicBoolean(false);
-
- private static FileObserver sPasswordObserver;
-
- private static class PasswordFileObserver extends FileObserver {
- public PasswordFileObserver(String path, int mask) {
- super(path, mask);
- }
-
- @Override
- public void onEvent(int event, String path) {
- if (LOCK_PATTERN_FILE.equals(path)) {
- Log.d(TAG, "lock pattern file changed");
- sHaveNonZeroPatternFile.set(new File(sLockPatternFilename).length() > 0);
- } else if (LOCK_PASSWORD_FILE.equals(path)) {
- Log.d(TAG, "lock password file changed");
- sHaveNonZeroPasswordFile.set(new File(sLockPasswordFilename).length() > 0);
- }
- }
- }
+ private ILockSettings mLockSettingsService;
+ private int mCurrentUserId = 0;
public DevicePolicyManager getDevicePolicyManager() {
if (mDevicePolicyManager == null) {
@@ -167,34 +146,27 @@ public class LockPatternUtils {
}
return mDevicePolicyManager;
}
+
/**
* @param contentResolver Used to look up and save settings.
*/
public LockPatternUtils(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
+ }
- // Initialize the location of gesture & PIN lock files
- if (sLockPatternFilename == null) {
- String dataSystemDirectory =
- android.os.Environment.getDataDirectory().getAbsolutePath() +
- SYSTEM_DIRECTORY;
- sLockPatternFilename = dataSystemDirectory + LOCK_PATTERN_FILE;
- sLockPasswordFilename = dataSystemDirectory + LOCK_PASSWORD_FILE;
- sHaveNonZeroPatternFile.set(new File(sLockPatternFilename).length() > 0);
- sHaveNonZeroPasswordFile.set(new File(sLockPasswordFilename).length() > 0);
- int fileObserverMask = FileObserver.CLOSE_WRITE | FileObserver.DELETE |
- FileObserver.MOVED_TO | FileObserver.CREATE;
- sPasswordObserver = new PasswordFileObserver(dataSystemDirectory, fileObserverMask);
- sPasswordObserver.startWatching();
+ private ILockSettings getLockSettings() {
+ if (mLockSettingsService == null) {
+ mLockSettingsService = ILockSettings.Stub.asInterface(
+ (IBinder) ServiceManager.getService("lock_settings"));
}
+ return mLockSettingsService;
}
public int getRequestedMinimumPasswordLength() {
return getDevicePolicyManager().getPasswordMinimumLength(null);
}
-
/**
* Gets the device policy password mode. If the mode is non-specific, returns
* MODE_PATTERN which allows the user to choose anything.
@@ -243,6 +215,33 @@ public class LockPatternUtils {
getDevicePolicyManager().reportSuccessfulPasswordAttempt();
}
+ public void setCurrentUser(int userId) {
+ if (Process.myUid() == Process.SYSTEM_UID) {
+ mCurrentUserId = userId;
+ } else {
+ throw new SecurityException("Only the system process can set the current user");
+ }
+ }
+
+ public void removeUser(int userId) {
+ if (Process.myUid() == Process.SYSTEM_UID) {
+ try {
+ getLockSettings().removeUser(userId);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Couldn't remove lock settings for user " + userId);
+ }
+ }
+ }
+
+ private int getCurrentOrCallingUserId() {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid == android.os.Process.SYSTEM_UID) {
+ return mCurrentUserId;
+ } else {
+ return UserId.getUserId(callingUid);
+ }
+ }
+
/**
* Check to see if a pattern matches the saved pattern. If no pattern exists,
* always returns true.
@@ -250,20 +249,10 @@ public class LockPatternUtils {
* @return Whether the pattern matches the stored one.
*/
public boolean checkPattern(List<LockPatternView.Cell> pattern) {
+ int userId = getCurrentOrCallingUserId();
try {
- // Read all the bytes from the file
- RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "r");
- final byte[] stored = new byte[(int) raf.length()];
- int got = raf.read(stored, 0, stored.length);
- raf.close();
- if (got <= 0) {
- return true;
- }
- // Compare the hash from the file with the entered pattern's hash
- return Arrays.equals(stored, LockPatternUtils.patternToHash(pattern));
- } catch (FileNotFoundException fnfe) {
- return true;
- } catch (IOException ioe) {
+ return getLockSettings().checkPattern(patternToHash(pattern), userId);
+ } catch (RemoteException re) {
return true;
}
}
@@ -275,20 +264,10 @@ public class LockPatternUtils {
* @return Whether the password matches the stored one.
*/
public boolean checkPassword(String password) {
+ int userId = getCurrentOrCallingUserId();
try {
- // Read all the bytes from the file
- RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "r");
- final byte[] stored = new byte[(int) raf.length()];
- int got = raf.read(stored, 0, stored.length);
- raf.close();
- if (got <= 0) {
- return true;
- }
- // Compare the hash from the file with the entered password's hash
- return Arrays.equals(stored, passwordToHash(password));
- } catch (FileNotFoundException fnfe) {
- return true;
- } catch (IOException ioe) {
+ return getLockSettings().checkPassword(passwordToHash(password), userId);
+ } catch (RemoteException re) {
return true;
}
}
@@ -325,7 +304,11 @@ public class LockPatternUtils {
* @return Whether a saved pattern exists.
*/
public boolean savedPatternExists() {
- return sHaveNonZeroPatternFile.get();
+ try {
+ return getLockSettings().havePattern(getCurrentOrCallingUserId());
+ } catch (RemoteException re) {
+ return false;
+ }
}
/**
@@ -333,7 +316,11 @@ public class LockPatternUtils {
* @return Whether a saved pattern exists.
*/
public boolean savedPasswordExists() {
- return sHaveNonZeroPasswordFile.get();
+ try {
+ return getLockSettings().havePassword(getCurrentOrCallingUserId());
+ } catch (RemoteException re) {
+ return false;
+ }
}
/**
@@ -471,15 +458,7 @@ public class LockPatternUtils {
// Compute the hash
final byte[] hash = LockPatternUtils.patternToHash(pattern);
try {
- // Write the hash to file
- RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "rw");
- // Truncate the file if pattern is null, to clear the lock
- if (pattern == null) {
- raf.setLength(0);
- } else {
- raf.write(hash, 0, hash.length);
- }
- raf.close();
+ getLockSettings().setLockPattern(hash, getCurrentOrCallingUserId());
DevicePolicyManager dpm = getDevicePolicyManager();
KeyStore keyStore = KeyStore.getInstance();
if (pattern != null) {
@@ -505,13 +484,8 @@ public class LockPatternUtils {
dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0,
0, 0, 0, 0, 0);
}
- } catch (FileNotFoundException fnfe) {
- // Cant do much, unless we want to fail over to using the settings
- // provider
- Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
- } catch (IOException ioe) {
- // Cant do much
- Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Couldn't save lock pattern " + re);
}
}
@@ -586,15 +560,7 @@ public class LockPatternUtils {
// Compute the hash
final byte[] hash = passwordToHash(password);
try {
- // Write the hash to file
- RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "rw");
- // Truncate the file if pattern is null, to clear the lock
- if (password == null) {
- raf.setLength(0);
- } else {
- raf.write(hash, 0, hash.length);
- }
- raf.close();
+ getLockSettings().setLockPassword(hash, getCurrentOrCallingUserId());
DevicePolicyManager dpm = getDevicePolicyManager();
KeyStore keyStore = KeyStore.getInstance();
if (password != null) {
@@ -676,12 +642,9 @@ public class LockPatternUtils {
dpm.setActivePasswordState(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0);
}
- } catch (FileNotFoundException fnfe) {
- // Cant do much, unless we want to fail over to using the settings provider
- Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
- } catch (IOException ioe) {
+ } catch (RemoteException re) {
// Cant do much
- Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
+ Log.e(TAG, "Unable to save lock password " + re);
}
}
@@ -1013,30 +976,57 @@ public class LockPatternUtils {
}
private boolean getBoolean(String secureSettingKey, boolean defaultValue) {
- return 1 ==
- android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey,
- defaultValue ? 1 : 0);
+ try {
+ return getLockSettings().getBoolean(secureSettingKey, defaultValue,
+ getCurrentOrCallingUserId());
+ } catch (RemoteException re) {
+ return defaultValue;
+ }
}
private void setBoolean(String secureSettingKey, boolean enabled) {
- android.provider.Settings.Secure.putInt(mContentResolver, secureSettingKey,
- enabled ? 1 : 0);
+ try {
+ getLockSettings().setBoolean(secureSettingKey, enabled, getCurrentOrCallingUserId());
+ } catch (RemoteException re) {
+ // What can we do?
+ Log.e(TAG, "Couldn't write boolean " + secureSettingKey + re);
+ }
}
- private long getLong(String secureSettingKey, long def) {
- return android.provider.Settings.Secure.getLong(mContentResolver, secureSettingKey, def);
+ private long getLong(String secureSettingKey, long defaultValue) {
+ try {
+ return getLockSettings().getLong(secureSettingKey, defaultValue,
+ getCurrentOrCallingUserId());
+ } catch (RemoteException re) {
+ return defaultValue;
+ }
}
private void setLong(String secureSettingKey, long value) {
- android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value);
+ try {
+ getLockSettings().setLong(secureSettingKey, value, getCurrentOrCallingUserId());
+ } catch (RemoteException re) {
+ // What can we do?
+ Log.e(TAG, "Couldn't write long " + secureSettingKey + re);
+ }
}
private String getString(String secureSettingKey) {
- return android.provider.Settings.Secure.getString(mContentResolver, secureSettingKey);
+ try {
+ return getLockSettings().getString(secureSettingKey, null,
+ getCurrentOrCallingUserId());
+ } catch (RemoteException re) {
+ return null;
+ }
}
private void setString(String secureSettingKey, String value) {
- android.provider.Settings.Secure.putString(mContentResolver, secureSettingKey, value);
+ try {
+ getLockSettings().setString(secureSettingKey, value, getCurrentOrCallingUserId());
+ } catch (RemoteException re) {
+ // What can we do?
+ Log.e(TAG, "Couldn't write string " + secureSettingKey + re);
+ }
}
public boolean isSecure() {
diff --git a/core/java/com/android/internal/widget/LockSettingsService.java b/core/java/com/android/internal/widget/LockSettingsService.java
new file mode 100644
index 000000000000..24c7161a1e14
--- /dev/null
+++ b/core/java/com/android/internal/widget/LockSettingsService.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.UserId;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Arrays;
+
+/**
+ * Keeps the lock pattern/password data and related settings for each user.
+ * Used by LockPatternUtils. Needs to be a service because Settings app also needs
+ * to be able to save lockscreen information for secondary users.
+ * @hide
+ */
+public class LockSettingsService extends ILockSettings.Stub {
+
+ private final DatabaseHelper mOpenHelper;
+ private static final String TAG = "LockSettingsService";
+
+ private static final String TABLE = "locksettings";
+ private static final String COLUMN_KEY = "name";
+ private static final String COLUMN_USERID = "user";
+ private static final String COLUMN_VALUE = "value";
+
+ private static final String[] COLUMNS_FOR_QUERY = {
+ COLUMN_VALUE
+ };
+
+ private static final String SYSTEM_DIRECTORY = "/system/";
+ private static final String LOCK_PATTERN_FILE = "gesture.key";
+ private static final String LOCK_PASSWORD_FILE = "password.key";
+
+ private final Context mContext;
+
+ public LockSettingsService(Context context) {
+ mContext = context;
+ // Open the database
+ mOpenHelper = new DatabaseHelper(mContext);
+ }
+
+ public void systemReady() {
+ migrateOldData();
+ }
+
+ private void migrateOldData() {
+ try {
+ if (getString("migrated", null, 0) != null) {
+ // Already migrated
+ return;
+ }
+
+ final ContentResolver cr = mContext.getContentResolver();
+ for (String validSetting : VALID_SETTINGS) {
+ String value = Settings.Secure.getString(cr, validSetting);
+ if (value != null) {
+ setString(validSetting, value, 0);
+ }
+ }
+ // No need to move the password / pattern files. They're already in the right place.
+ setString("migrated", "true", 0);
+ Slog.i(TAG, "Migrated lock settings to new location");
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to migrate old data");
+ }
+ }
+
+ private static final void checkWritePermission(int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
+ throw new SecurityException("uid=" + callingUid
+ + " not authorized to write lock settings");
+ }
+ }
+
+ private static final void checkPasswordReadPermission(int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID) {
+ throw new SecurityException("uid=" + callingUid
+ + " not authorized to read lock password");
+ }
+ }
+
+ private static final void checkReadPermission(int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (UserId.getAppId(callingUid) != android.os.Process.SYSTEM_UID
+ && UserId.getUserId(callingUid) != userId) {
+ throw new SecurityException("uid=" + callingUid
+ + " not authorized to read settings of user " + userId);
+ }
+ }
+
+ @Override
+ public void setBoolean(String key, boolean value, int userId) throws RemoteException {
+ checkWritePermission(userId);
+
+ writeToDb(key, value ? "1" : "0", userId);
+ }
+
+ @Override
+ public void setLong(String key, long value, int userId) throws RemoteException {
+ checkWritePermission(userId);
+
+ writeToDb(key, Long.toString(value), userId);
+ }
+
+ @Override
+ public void setString(String key, String value, int userId) throws RemoteException {
+ checkWritePermission(userId);
+
+ writeToDb(key, value, userId);
+ }
+
+ @Override
+ public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
+ //checkReadPermission(userId);
+
+ String value = readFromDb(key, null, userId);
+ return TextUtils.isEmpty(value) ?
+ defaultValue : (value.equals("1") || value.equals("true"));
+ }
+
+ @Override
+ public long getLong(String key, long defaultValue, int userId) throws RemoteException {
+ //checkReadPermission(userId);
+
+ String value = readFromDb(key, null, userId);
+ return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
+ }
+
+ @Override
+ public String getString(String key, String defaultValue, int userId) throws RemoteException {
+ //checkReadPermission(userId);
+
+ return readFromDb(key, defaultValue, userId);
+ }
+
+ private String getLockPatternFilename(int userId) {
+ String dataSystemDirectory =
+ android.os.Environment.getDataDirectory().getAbsolutePath() +
+ SYSTEM_DIRECTORY;
+ if (userId == 0) {
+ // Leave it in the same place for user 0
+ return dataSystemDirectory + LOCK_PATTERN_FILE;
+ } else {
+ return dataSystemDirectory + "users/" + userId + "/" + LOCK_PATTERN_FILE;
+ }
+ }
+
+ private String getLockPasswordFilename(int userId) {
+ String dataSystemDirectory =
+ android.os.Environment.getDataDirectory().getAbsolutePath() +
+ SYSTEM_DIRECTORY;
+ if (userId == 0) {
+ // Leave it in the same place for user 0
+ return dataSystemDirectory + LOCK_PASSWORD_FILE;
+ } else {
+ return dataSystemDirectory + "users/" + userId + "/" + LOCK_PASSWORD_FILE;
+ }
+ }
+
+ @Override
+ public boolean havePassword(int userId) throws RemoteException {
+ // Do we need a permissions check here?
+
+ return new File(getLockPasswordFilename(userId)).length() > 0;
+ }
+
+ @Override
+ public boolean havePattern(int userId) throws RemoteException {
+ // Do we need a permissions check here?
+
+ return new File(getLockPatternFilename(userId)).length() > 0;
+ }
+
+ @Override
+ public void setLockPattern(byte[] hash, int userId) throws RemoteException {
+ checkWritePermission(userId);
+
+ writeFile(getLockPatternFilename(userId), hash);
+ }
+
+ @Override
+ public boolean checkPattern(byte[] hash, int userId) throws RemoteException {
+ checkPasswordReadPermission(userId);
+ try {
+ // Read all the bytes from the file
+ RandomAccessFile raf = new RandomAccessFile(getLockPatternFilename(userId), "r");
+ final byte[] stored = new byte[(int) raf.length()];
+ int got = raf.read(stored, 0, stored.length);
+ raf.close();
+ if (got <= 0) {
+ return true;
+ }
+ // Compare the hash from the file with the entered pattern's hash
+ return Arrays.equals(stored, hash);
+ } catch (FileNotFoundException fnfe) {
+ Slog.e(TAG, "Cannot read file " + fnfe);
+ return true;
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Cannot read file " + ioe);
+ return true;
+ }
+ }
+
+ @Override
+ public void setLockPassword(byte[] hash, int userId) throws RemoteException {
+ checkWritePermission(userId);
+
+ writeFile(getLockPasswordFilename(userId), hash);
+ }
+
+ @Override
+ public boolean checkPassword(byte[] hash, int userId) throws RemoteException {
+ checkPasswordReadPermission(userId);
+
+ try {
+ // Read all the bytes from the file
+ RandomAccessFile raf = new RandomAccessFile(getLockPasswordFilename(userId), "r");
+ final byte[] stored = new byte[(int) raf.length()];
+ int got = raf.read(stored, 0, stored.length);
+ raf.close();
+ if (got <= 0) {
+ return true;
+ }
+ // Compare the hash from the file with the entered password's hash
+ return Arrays.equals(stored, hash);
+ } catch (FileNotFoundException fnfe) {
+ Slog.e(TAG, "Cannot read file " + fnfe);
+ return true;
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Cannot read file " + ioe);
+ return true;
+ }
+ }
+
+ @Override
+ public void removeUser(int userId) {
+ checkWritePermission(userId);
+
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ try {
+ File file = new File(getLockPasswordFilename(userId));
+ if (file.exists()) {
+ file.delete();
+ }
+ file = new File(getLockPatternFilename(userId));
+ if (file.exists()) {
+ file.delete();
+ }
+
+ db.beginTransaction();
+ db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ private void writeFile(String name, byte[] hash) {
+ try {
+ // Write the hash to file
+ RandomAccessFile raf = new RandomAccessFile(name, "rw");
+ // Truncate the file if pattern is null, to clear the lock
+ if (hash == null || hash.length == 0) {
+ raf.setLength(0);
+ } else {
+ raf.write(hash, 0, hash.length);
+ }
+ raf.close();
+ } catch (IOException ioe) {
+ Slog.e(TAG, "Error writing to file " + ioe);
+ }
+ }
+
+ private void writeToDb(String key, String value, int userId) {
+ ContentValues cv = new ContentValues();
+ cv.put(COLUMN_KEY, key);
+ cv.put(COLUMN_USERID, userId);
+ cv.put(COLUMN_VALUE, value);
+
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?",
+ new String[] {key, Integer.toString(userId)});
+ db.insert(TABLE, null, cv);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ private String readFromDb(String key, String defaultValue, int userId) {
+ Cursor cursor;
+ String result = defaultValue;
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY,
+ COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?",
+ new String[] { Integer.toString(userId), key },
+ null, null, null)) != null) {
+ if (cursor.moveToFirst()) {
+ result = cursor.getString(0);
+ }
+ cursor.close();
+ }
+ return result;
+ }
+
+ class DatabaseHelper extends SQLiteOpenHelper {
+ private static final String TAG = "LockSettingsDB";
+ private static final String DATABASE_NAME = "locksettings.db";
+
+ private static final int DATABASE_VERSION = 1;
+
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ setWriteAheadLoggingEnabled(true);
+ }
+
+ private void createTable(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE + " (" +
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
+ COLUMN_KEY + " TEXT," +
+ COLUMN_USERID + " INTEGER," +
+ COLUMN_VALUE + " TEXT" +
+ ");");
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ createTable(db);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
+ // Nothing yet
+ }
+ }
+
+ private static final String[] VALID_SETTINGS = new String[] {
+ LockPatternUtils.LOCKOUT_PERMANENT_KEY,
+ LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
+ LockPatternUtils.PATTERN_EVER_CHOSEN_KEY,
+ LockPatternUtils.PASSWORD_TYPE_KEY,
+ LockPatternUtils.PASSWORD_TYPE_ALTERNATE_KEY,
+ LockPatternUtils.LOCK_PASSWORD_SALT_KEY,
+ LockPatternUtils.DISABLE_LOCKSCREEN_KEY,
+ LockPatternUtils.LOCKSCREEN_OPTIONS,
+ LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK,
+ LockPatternUtils.BIOMETRIC_WEAK_EVER_CHOSEN_KEY,
+ LockPatternUtils.LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS,
+ LockPatternUtils.PASSWORD_HISTORY_KEY,
+ Secure.LOCK_PATTERN_ENABLED,
+ Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
+ Secure.LOCK_PATTERN_VISIBLE,
+ Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
+ };
+}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index 804cd9efe5e8..a4723751d74c 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -105,6 +105,7 @@ public class KeyguardUpdateMonitor {
private static final int MSG_CLOCK_VISIBILITY_CHANGED = 307;
private static final int MSG_DEVICE_PROVISIONED = 308;
protected static final int MSG_DPM_STATE_CHANGED = 309;
+ protected static final int MSG_USER_CHANGED = 310;
/**
* When we receive a
@@ -209,6 +210,9 @@ public class KeyguardUpdateMonitor {
case MSG_DPM_STATE_CHANGED:
handleDevicePolicyManagerStateChanged();
break;
+ case MSG_USER_CHANGED:
+ handleUserChanged(msg.arg1);
+ break;
}
}
};
@@ -268,6 +272,8 @@ public class KeyguardUpdateMonitor {
filter.addAction(SPN_STRINGS_UPDATED_ACTION);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
context.registerReceiver(new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
@@ -302,6 +308,9 @@ public class KeyguardUpdateMonitor {
} else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED));
+ } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_CHANGED,
+ intent.getIntExtra(Intent.EXTRA_USERID, 0), 0));
}
}
}, filter);
@@ -313,6 +322,12 @@ public class KeyguardUpdateMonitor {
}
}
+ protected void handleUserChanged(int userId) {
+ for (int i = 0; i < mInfoCallbacks.size(); i++) {
+ mInfoCallbacks.get(i).onUserChanged(userId);
+ }
+ }
+
protected void handleDeviceProvisioned() {
for (int i = 0; i < mInfoCallbacks.size(); i++) {
mInfoCallbacks.get(i).onDeviceProvisioned();
@@ -542,6 +557,11 @@ public class KeyguardUpdateMonitor {
* See {@link DevicePolicyManager#ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED}
*/
void onDevicePolicyManagerStateChanged();
+
+ /**
+ * Called when the user changes.
+ */
+ void onUserChanged(int userId);
}
// Simple class that allows methods to easily be overwritten
@@ -570,6 +590,9 @@ public class KeyguardUpdateMonitor {
public void onDevicePolicyManagerStateChanged() {
}
+
+ public void onUserChanged(int userId) {
+ }
}
/**
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 377ea66406cd..0031484995c2 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -342,6 +342,10 @@ public class KeyguardViewMediator implements KeyguardViewCallback,
if (soundPath == null || mUnlockSoundId == 0) {
if (DEBUG) Log.d(TAG, "failed to load sound from " + soundPath);
}
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_SWITCHED);
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(mUserChangeReceiver, userFilter);
}
/**
@@ -801,6 +805,29 @@ public class KeyguardViewMediator implements KeyguardViewCallback,
return mKeyguardViewProperties.isSecure();
}
+ private void onUserSwitched(int userId) {
+ mLockPatternUtils.setCurrentUser(userId);
+ synchronized (KeyguardViewMediator.this) {
+ resetStateLocked();
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ mLockPatternUtils.removeUser(userId);
+ }
+
+ private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ onUserSwitched(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ onUserRemoved(intent.getIntExtra(Intent.EXTRA_USERID, 0));
+ }
+ }
+ };
+
private BroadcastReceiver mBroadCastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 2e7769b44d79..404dc6f5924b 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -757,6 +757,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler
hideFaceLockArea();
}
}
+
+ @Override
+ public void onUserChanged(int userId) {
+ mLockPatternUtils.setCurrentUser(userId);
+ updateScreen(getInitialMode(), true);
+ }
};
@Override
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e091edfe8a3e..00f80babece5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -47,6 +47,7 @@ import android.view.WindowManager;
import com.android.internal.app.ShutdownThread;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
+import com.android.internal.widget.LockSettingsService;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
import com.android.server.net.NetworkPolicyManagerService;
@@ -266,6 +267,7 @@ class ServerThread extends Thread {
LocationManagerService location = null;
CountryDetectorService countryDetector = null;
TextServicesManagerService tsms = null;
+ LockSettingsService lockSettings = null;
// Bring up services needed for UI.
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
@@ -308,6 +310,14 @@ class ServerThread extends Thread {
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
+ Slog.i(TAG, "LockSettingsService");
+ lockSettings = new LockSettingsService(context);
+ ServiceManager.addService("lock_settings", lockSettings);
+ } catch (Throwable e) {
+ reportWtf("starting LockSettingsService service", e);
+ }
+
+ try {
Slog.i(TAG, "Device Policy");
devicePolicy = new DevicePolicyManagerService(context);
ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
@@ -661,6 +671,11 @@ class ServerThread extends Thread {
} catch (Throwable e) {
reportWtf("making Package Manager Service ready", e);
}
+ try {
+ lockSettings.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Lock Settings Service ready", e);
+ }
// These are needed to propagate to the runnable below.
final Context contextF = context;