/* * 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 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; } } }