1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.flags; 18 19 import java.util.HashMap; 20 import java.util.Map; 21 import java.util.function.Function; 22 23 /** 24 * Threadsafe cache of values that stores the supplied default on cache miss. 25 * 26 * @param <V> The type of value to store. 27 */ 28 public class FlagCache<V> { 29 private final Function<String, HashMap<String, V>> mNewHashMap = k -> new HashMap<>(); 30 31 // Cache is organized first by namespace, then by name. All values are stored as strings. 32 final Map<String, Map<String, V>> mCache = new HashMap<>(); 33 FlagCache()34 FlagCache() { 35 } 36 37 /** 38 * Returns true if the namespace exists in the cache already. 39 */ containsNamespace(String namespace)40 boolean containsNamespace(String namespace) { 41 synchronized (mCache) { 42 return mCache.containsKey(namespace); 43 } 44 } 45 46 /** 47 * Returns true if the value is stored in the cache. 48 */ contains(String namespace, String name)49 boolean contains(String namespace, String name) { 50 synchronized (mCache) { 51 Map<String, V> nsCache = mCache.get(namespace); 52 return nsCache != null && nsCache.containsKey(name); 53 } 54 } 55 56 /** 57 * Sets the value if it is different from what is currently stored. 58 * 59 * If the value is not set, or the current value is null, it will store the value and 60 * return true. 61 * 62 * @return True if the value was set. False if the value is the same. 63 */ setIfChanged(String namespace, String name, V value)64 boolean setIfChanged(String namespace, String name, V value) { 65 synchronized (mCache) { 66 Map<String, V> nsCache = mCache.computeIfAbsent(namespace, mNewHashMap); 67 V curValue = nsCache.get(name); 68 if (curValue == null || !curValue.equals(value)) { 69 nsCache.put(name, value); 70 return true; 71 } 72 return false; 73 } 74 } 75 76 /** 77 * Gets the current value from the cache, setting it if it is currently absent. 78 * 79 * @return The value that is now in the cache after the call to the method. 80 */ getOrSet(String namespace, String name, V defaultValue)81 V getOrSet(String namespace, String name, V defaultValue) { 82 synchronized (mCache) { 83 Map<String, V> nsCache = mCache.computeIfAbsent(namespace, mNewHashMap); 84 V value = nsCache.putIfAbsent(name, defaultValue); 85 return value == null ? defaultValue : value; 86 } 87 } 88 89 /** 90 * Gets the current value from the cache, returning null if not present. 91 * 92 * @return The value that is now in the cache if there is one. 93 */ getOrNull(String namespace, String name)94 V getOrNull(String namespace, String name) { 95 synchronized (mCache) { 96 Map<String, V> nsCache = mCache.get(namespace); 97 if (nsCache == null) { 98 return null; 99 } 100 return nsCache.get(name); 101 } 102 } 103 } 104