blob: 624f67c0f729b713bdd658ade5dec9ce01d1f615 [file] [log] [blame]
/*
* 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 final String HAS_LOCK_PATTERN_CACHE_KEY
= "LockPatternUtils.Cache.HasLockPatternCacheKey";
private static final String HAS_LOCK_PASSWORD_CACHE_KEY
= "LockPatternUtils.Cache.HasLockPasswordCacheKey";
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 {
invalidateCache(HAS_LOCK_PATTERN_CACHE_KEY, userId);
mService.setLockPattern(pattern, userId);
putCache(HAS_LOCK_PATTERN_CACHE_KEY, userId, pattern != null);
}
@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 {
invalidateCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId);
mService.setLockPassword(password, userId);
putCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId, password != null);
}
@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 {
Object value = peekCache(HAS_LOCK_PATTERN_CACHE_KEY, userId);
if (value instanceof Boolean) {
return (boolean) value;
}
boolean result = mService.havePattern(userId);
putCache(HAS_LOCK_PATTERN_CACHE_KEY, userId, result);
return result;
}
@Override
public boolean havePassword(int userId) throws RemoteException {
Object value = peekCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId);
if (value instanceof Boolean) {
return (boolean) value;
}
boolean result = mService.havePassword(userId);
putCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId, result);
return result;
}
@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;
}
}
}