Merge "Add a cache to LockPatternUtils" into lmp-preview-dev
This commit is contained in:
@@ -276,6 +276,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/com/android/internal/view/IInputMethodSession.aidl \
|
||||
core/java/com/android/internal/view/IInputSessionCallback.aidl \
|
||||
core/java/com/android/internal/widget/ILockSettings.aidl \
|
||||
core/java/com/android/internal/widget/ILockSettingsObserver.aidl \
|
||||
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
|
||||
core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
|
||||
keystore/java/android/security/IKeyChainAliasCallback.aidl \
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.internal.widget;
|
||||
|
||||
import com.android.internal.widget.ILockSettingsObserver;
|
||||
|
||||
/** {@hide} */
|
||||
interface ILockSettings {
|
||||
void setBoolean(in String key, in boolean value, in int userId);
|
||||
@@ -32,4 +34,6 @@ interface ILockSettings {
|
||||
boolean havePattern(int userId);
|
||||
boolean havePassword(int userId);
|
||||
void removeUser(int userId);
|
||||
void registerObserver(in ILockSettingsObserver observer);
|
||||
void unregisterObserver(in ILockSettingsObserver observer);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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} */
|
||||
oneway interface ILockSettingsObserver {
|
||||
void onLockSettingChanged(in String key, in int userId);
|
||||
}
|
||||
@@ -199,8 +199,8 @@ public class LockPatternUtils {
|
||||
|
||||
private ILockSettings getLockSettings() {
|
||||
if (mLockSettingsService == null) {
|
||||
mLockSettingsService = ILockSettings.Stub.asInterface(
|
||||
(IBinder) ServiceManager.getService("lock_settings"));
|
||||
mLockSettingsService = LockPatternUtilsCache.getInstance(
|
||||
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings")));
|
||||
}
|
||||
return mLockSettingsService;
|
||||
}
|
||||
|
||||
222
core/java/com/android/internal/widget/LockPatternUtilsCache.java
Normal file
222
core/java/com/android/internal/widget/LockPatternUtilsCache.java
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
/**
|
||||
* A decorator for {@link ILockSettings} that caches the key-value responses in memory.
|
||||
*
|
||||
* Specifically, the return values of {@link #getString(String, String, int)},
|
||||
* {@link #getLong(String, long, int)} and {@link #getBoolean(String, boolean, int)} are cached.
|
||||
*/
|
||||
public class LockPatternUtilsCache implements ILockSettings {
|
||||
|
||||
private static LockPatternUtilsCache sInstance;
|
||||
|
||||
private final ILockSettings mService;
|
||||
|
||||
/** Only access when holding {@code mCache} lock. */
|
||||
private final ArrayMap<CacheKey, Object> mCache = new ArrayMap<>();
|
||||
|
||||
/** Only access when holding {@link #mCache} lock. */
|
||||
private final CacheKey mCacheKey = new CacheKey();
|
||||
|
||||
|
||||
public static synchronized LockPatternUtilsCache getInstance(ILockSettings service) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new LockPatternUtilsCache(service);
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
// ILockSettings
|
||||
|
||||
private LockPatternUtilsCache(ILockSettings service) {
|
||||
mService = service;
|
||||
try {
|
||||
service.registerObserver(mObserver);
|
||||
} catch (RemoteException e) {
|
||||
// Not safe to do caching without the observer. System process has probably died
|
||||
// anyway, so crashing here is fine.
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBoolean(String key, boolean value, int userId) throws RemoteException {
|
||||
invalidateCache(key, userId);
|
||||
mService.setBoolean(key, value, userId);
|
||||
putCache(key, userId, value);
|
||||
}
|
||||
|
||||
public void setLong(String key, long value, int userId) throws RemoteException {
|
||||
invalidateCache(key, userId);
|
||||
mService.setLong(key, value, userId);
|
||||
putCache(key, userId, value);
|
||||
}
|
||||
|
||||
public void setString(String key, String value, int userId) throws RemoteException {
|
||||
invalidateCache(key, userId);
|
||||
mService.setString(key, value, userId);
|
||||
putCache(key, userId, value);
|
||||
}
|
||||
|
||||
public long getLong(String key, long defaultValue, int userId) throws RemoteException {
|
||||
Object value = peekCache(key, userId);
|
||||
if (value instanceof Long) {
|
||||
return (long) value;
|
||||
}
|
||||
long result = mService.getLong(key, defaultValue, userId);
|
||||
putCache(key, userId, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getString(String key, String defaultValue, int userId) throws RemoteException {
|
||||
Object value = peekCache(key, userId);
|
||||
if (value instanceof String) {
|
||||
return (String) value;
|
||||
}
|
||||
String result = mService.getString(key, defaultValue, userId);
|
||||
putCache(key, userId, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
|
||||
Object value = peekCache(key, userId);
|
||||
if (value instanceof Boolean) {
|
||||
return (boolean) value;
|
||||
}
|
||||
boolean result = mService.getBoolean(key, defaultValue, userId);
|
||||
putCache(key, userId, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLockPattern(String pattern, int userId) throws RemoteException {
|
||||
mService.setLockPattern(pattern, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPattern(String pattern, int userId) throws RemoteException {
|
||||
return mService.checkPattern(pattern, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLockPassword(String password, int userId) throws RemoteException {
|
||||
mService.setLockPassword(password, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPassword(String password, int userId) throws RemoteException {
|
||||
return mService.checkPassword(password, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkVoldPassword(int userId) throws RemoteException {
|
||||
return mService.checkVoldPassword(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean havePattern(int userId) throws RemoteException {
|
||||
return mService.havePattern(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean havePassword(int userId) throws RemoteException {
|
||||
return mService.havePassword(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUser(int userId) throws RemoteException {
|
||||
mService.removeUser(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerObserver(ILockSettingsObserver observer) throws RemoteException {
|
||||
mService.registerObserver(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObserver(ILockSettingsObserver observer) throws RemoteException {
|
||||
mService.unregisterObserver(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder asBinder() {
|
||||
return mService.asBinder();
|
||||
}
|
||||
|
||||
// Caching
|
||||
|
||||
private Object peekCache(String key, int userId) {
|
||||
synchronized (mCache) {
|
||||
// Safe to reuse mCacheKey, because it is not stored in the map.
|
||||
return mCache.get(mCacheKey.set(key, userId));
|
||||
}
|
||||
}
|
||||
|
||||
private void putCache(String key, int userId, Object value) {
|
||||
synchronized (mCache) {
|
||||
// Create a new key, because this will be stored in the map.
|
||||
mCache.put(new CacheKey().set(key, userId), value);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateCache(String key, int userId) {
|
||||
synchronized (mCache) {
|
||||
// Safe to reuse mCacheKey, because it is not stored in the map.
|
||||
mCache.remove(mCacheKey.set(key, userId));
|
||||
}
|
||||
}
|
||||
|
||||
private final ILockSettingsObserver mObserver = new ILockSettingsObserver.Stub() {
|
||||
@Override
|
||||
public void onLockSettingChanged(String key, int userId) throws RemoteException {
|
||||
invalidateCache(key, userId);
|
||||
}
|
||||
};
|
||||
|
||||
private static final class CacheKey {
|
||||
String key;
|
||||
int userId;
|
||||
|
||||
public CacheKey set(String key, int userId) {
|
||||
this.key = key;
|
||||
this.userId = userId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CacheKey copy() {
|
||||
return new CacheKey().set(key, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof CacheKey))
|
||||
return false;
|
||||
CacheKey o = (CacheKey) obj;
|
||||
return userId == o.userId && key.equals(o.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return key.hashCode() ^ userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,12 +47,14 @@ import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.widget.ILockSettings;
|
||||
import com.android.internal.widget.ILockSettingsObserver;
|
||||
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.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -65,6 +67,9 @@ import java.util.List;
|
||||
public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
private static final String PERMISSION = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE";
|
||||
|
||||
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
|
||||
|
||||
private final DatabaseHelper mOpenHelper;
|
||||
private static final String TAG = "LockSettingsService";
|
||||
|
||||
@@ -85,6 +90,8 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
private LockPatternUtils mLockPatternUtils;
|
||||
private boolean mFirstCallToVold;
|
||||
|
||||
private final ArrayList<LockSettingsObserver> mObservers = new ArrayList<>();
|
||||
|
||||
public LockSettingsService(Context context) {
|
||||
mContext = context;
|
||||
// Open the database
|
||||
@@ -222,6 +229,52 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
return readFromDb(key, defaultValue, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerObserver(ILockSettingsObserver remote) throws RemoteException {
|
||||
synchronized (mObservers) {
|
||||
for (int i = 0; i < mObservers.size(); i++) {
|
||||
if (mObservers.get(i).remote.asBinder() == remote.asBinder()) {
|
||||
boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
|
||||
if (isDebuggable) {
|
||||
throw new IllegalStateException("Observer was already registered.");
|
||||
} else {
|
||||
Log.e(TAG, "Observer was already registered.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
LockSettingsObserver o = new LockSettingsObserver();
|
||||
o.remote = remote;
|
||||
o.remote.asBinder().linkToDeath(o, 0);
|
||||
mObservers.add(o);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterObserver(ILockSettingsObserver remote) throws RemoteException {
|
||||
synchronized (mObservers) {
|
||||
for (int i = 0; i < mObservers.size(); i++) {
|
||||
if (mObservers.get(i).remote.asBinder() == remote.asBinder()) {
|
||||
mObservers.remove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyObservers(String key, int userId) {
|
||||
synchronized (mObservers) {
|
||||
for (int i = 0; i < mObservers.size(); i++) {
|
||||
try {
|
||||
mObservers.get(i).remote.onLockSettingChanged(key, userId);
|
||||
} catch (RemoteException e) {
|
||||
// The stack trace is not really helpful here.
|
||||
Log.e(TAG, "Failed to notify ILockSettingsObserver: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getLockPatternFilename(int userId) {
|
||||
String dataSystemDirectory =
|
||||
android.os.Environment.getDataDirectory().getAbsolutePath() +
|
||||
@@ -438,6 +491,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
|
||||
private void writeToDb(String key, String value, int userId) {
|
||||
writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId);
|
||||
notifyObservers(key, userId);
|
||||
}
|
||||
|
||||
private void writeToDb(SQLiteDatabase db, String key, String value, int userId) {
|
||||
@@ -583,4 +637,13 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private class LockSettingsObserver implements DeathRecipient {
|
||||
ILockSettingsObserver remote;
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
mObservers.remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user