This is in preparation to moving keyguard into its own process. Moved keyguard source and resources into new .apk. Got basic test app working. Still need to implement MockPatternUtils and means to pass it into KeyguardService with local binder interface. Added new ACCESS_KEYGUARD_SECURE_STORAGE permission. Temporarily disabled USER_PRESENT broadcast. Remove unintentional whitespace changes in PhoneWindowManager, etc. Checkpoint basic working version. Move to systemui process. Synchronize with TOT. Sync with recent user API changes. Fix bug with returing interface instead of stub for IKeyguardResult. Create KeyguardServiceDelegate to allow for runtime-selectable local or remote interface. More keyguard crash robustness. Keyguard crash recovery working. Currently fails safe (locked). Fix selector view which was still using frameworks resources. Remove more references to internal framework variables. Use aliases for those we should move but currently have dependencies. Allow runtime switching between service and local mode. Fix layout issue on tablets where orientation was reading the incorrect constant from the framework. Remove more framework dependencies. Fix PIN keyboard input. Remove unnecessary copy of orientation attrs. Remove unused user selector widget and attempt to get multi user working again. Fix multi-user avatar icon by grabbing it from UserManager rather than directly since keyguard can no longer read it. Merge with AppWidget userId changes in master. Change-Id: I254d6fc6423ae40f6d7fef50aead4caa701e5ad2
393 lines
13 KiB
Java
393 lines
13 KiB
Java
/*
|
|
* 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.server;
|
|
|
|
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.Environment;
|
|
import android.os.RemoteException;
|
|
import android.os.SystemProperties;
|
|
import android.os.UserHandle;
|
|
import android.provider.Settings;
|
|
import android.provider.Settings.Secure;
|
|
import android.text.TextUtils;
|
|
import android.util.Slog;
|
|
|
|
import com.android.internal.widget.ILockSettings;
|
|
import com.android.internal.widget.LockPatternUtils;
|
|
|
|
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 static final String PERMISSION = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE";
|
|
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 final void checkWritePermission(int userId) {
|
|
mContext.checkCallingOrSelfPermission(PERMISSION);
|
|
}
|
|
|
|
private final void checkPasswordReadPermission(int userId) {
|
|
mContext.checkCallingOrSelfPermission(PERMISSION);
|
|
}
|
|
|
|
@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 new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE)
|
|
.getAbsolutePath();
|
|
}
|
|
}
|
|
|
|
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 new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
|
|
.getAbsolutePath();
|
|
}
|
|
}
|
|
|
|
@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) {
|
|
writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId);
|
|
}
|
|
|
|
private void writeToDb(SQLiteDatabase db, 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);
|
|
|
|
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);
|
|
initializeDefaults(db);
|
|
}
|
|
|
|
private void initializeDefaults(SQLiteDatabase db) {
|
|
// Get the lockscreen default from a system property, if available
|
|
boolean lockScreenDisable = SystemProperties.getBoolean("ro.lockscreen.disable.default",
|
|
false);
|
|
if (lockScreenDisable) {
|
|
writeToDb(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
|
|
}
|
|
}
|
|
|
|
@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
|
|
};
|
|
}
|