1 /* 2 * Copyright (C) 2019 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 package android.view.textclassifier; 17 18 import android.annotation.Nullable; 19 import android.provider.DeviceConfig; 20 import android.util.ArrayMap; 21 import android.util.KeyValueListParser; 22 23 import com.android.internal.annotations.GuardedBy; 24 import com.android.internal.annotations.VisibleForTesting; 25 import com.android.internal.annotations.VisibleForTesting.Visibility; 26 import com.android.internal.util.Preconditions; 27 28 import java.util.Arrays; 29 import java.util.Collections; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.function.Supplier; 33 34 /** 35 * Retrieves settings from {@link DeviceConfig} and {@link android.provider.Settings}. 36 * It will try DeviceConfig first and then Settings. 37 * 38 * @hide 39 */ 40 @VisibleForTesting(visibility = Visibility.PACKAGE) 41 public final class ConfigParser { 42 private static final String TAG = "ConfigParser"; 43 44 static final boolean ENABLE_DEVICE_CONFIG = true; 45 46 private static final String STRING_LIST_DELIMITER = ":"; 47 48 private final Supplier<String> mLegacySettingsSupplier; 49 private final Object mLock = new Object(); 50 @GuardedBy("mLock") 51 private final Map<String, Object> mCache = new ArrayMap<>(); 52 @GuardedBy("mLock") 53 private @Nullable KeyValueListParser mSettingsParser; // Call getLegacySettings() instead. 54 ConfigParser(Supplier<String> legacySettingsSupplier)55 public ConfigParser(Supplier<String> legacySettingsSupplier) { 56 mLegacySettingsSupplier = Preconditions.checkNotNull(legacySettingsSupplier); 57 } 58 getLegacySettings()59 private KeyValueListParser getLegacySettings() { 60 synchronized (mLock) { 61 if (mSettingsParser == null) { 62 final String legacySettings = mLegacySettingsSupplier.get(); 63 try { 64 mSettingsParser = new KeyValueListParser(','); 65 mSettingsParser.setString(legacySettings); 66 } catch (IllegalArgumentException e) { 67 // Failed to parse the settings string, log this and move on with defaults. 68 Log.w(TAG, "Bad text_classifier_constants: " + legacySettings); 69 } 70 } 71 return mSettingsParser; 72 } 73 } 74 75 /** 76 * Reads a boolean setting through the cache. 77 */ getBoolean(String key, boolean defaultValue)78 public boolean getBoolean(String key, boolean defaultValue) { 79 synchronized (mLock) { 80 final Object cached = mCache.get(key); 81 if (cached instanceof Boolean) { 82 return (boolean) cached; 83 } 84 final boolean value; 85 if (ENABLE_DEVICE_CONFIG) { 86 value = DeviceConfig.getBoolean( 87 DeviceConfig.NAMESPACE_TEXTCLASSIFIER, 88 key, 89 getLegacySettings().getBoolean(key, defaultValue)); 90 } else { 91 value = getLegacySettings().getBoolean(key, defaultValue); 92 } 93 mCache.put(key, value); 94 return value; 95 } 96 } 97 98 /** 99 * Reads an integer setting through the cache. 100 */ getInt(String key, int defaultValue)101 public int getInt(String key, int defaultValue) { 102 synchronized (mLock) { 103 final Object cached = mCache.get(key); 104 if (cached instanceof Integer) { 105 return (int) cached; 106 } 107 final int value; 108 if (ENABLE_DEVICE_CONFIG) { 109 value = DeviceConfig.getInt( 110 DeviceConfig.NAMESPACE_TEXTCLASSIFIER, 111 key, 112 getLegacySettings().getInt(key, defaultValue)); 113 } else { 114 value = getLegacySettings().getInt(key, defaultValue); 115 } 116 mCache.put(key, value); 117 return value; 118 } 119 } 120 121 /** 122 * Reads a float setting through the cache. 123 */ getFloat(String key, float defaultValue)124 public float getFloat(String key, float defaultValue) { 125 synchronized (mLock) { 126 final Object cached = mCache.get(key); 127 if (cached instanceof Float) { 128 return (float) cached; 129 } 130 final float value; 131 if (ENABLE_DEVICE_CONFIG) { 132 value = DeviceConfig.getFloat( 133 DeviceConfig.NAMESPACE_TEXTCLASSIFIER, 134 key, 135 getLegacySettings().getFloat(key, defaultValue)); 136 } else { 137 value = getLegacySettings().getFloat(key, defaultValue); 138 } 139 mCache.put(key, value); 140 return value; 141 } 142 } 143 144 /** 145 * Reads a string setting through the cache. 146 */ getString(String key, String defaultValue)147 public String getString(String key, String defaultValue) { 148 synchronized (mLock) { 149 final Object cached = mCache.get(key); 150 if (cached instanceof String) { 151 return (String) cached; 152 } 153 final String value; 154 if (ENABLE_DEVICE_CONFIG) { 155 value = DeviceConfig.getString( 156 DeviceConfig.NAMESPACE_TEXTCLASSIFIER, 157 key, 158 getLegacySettings().getString(key, defaultValue)); 159 } else { 160 value = getLegacySettings().getString(key, defaultValue); 161 } 162 mCache.put(key, value); 163 return value; 164 } 165 } 166 167 /** 168 * Reads a string list setting through the cache. 169 */ getStringList(String key, List<String> defaultValue)170 public List<String> getStringList(String key, List<String> defaultValue) { 171 synchronized (mLock) { 172 final Object cached = mCache.get(key); 173 if (cached instanceof List) { 174 final List asList = (List) cached; 175 if (asList.isEmpty()) { 176 return Collections.emptyList(); 177 } else if (asList.get(0) instanceof String) { 178 return (List<String>) cached; 179 } 180 } 181 final List<String> value; 182 if (ENABLE_DEVICE_CONFIG) { 183 value = getDeviceConfigStringList( 184 key, 185 getSettingsStringList(key, defaultValue)); 186 } else { 187 value = getSettingsStringList(key, defaultValue); 188 } 189 mCache.put(key, value); 190 return value; 191 } 192 } 193 194 /** 195 * Reads a float array through the cache. The returned array should be expected to be of the 196 * same length as that of the defaultValue. 197 */ getFloatArray(String key, float[] defaultValue)198 public float[] getFloatArray(String key, float[] defaultValue) { 199 synchronized (mLock) { 200 final Object cached = mCache.get(key); 201 if (cached instanceof float[]) { 202 return (float[]) cached; 203 } 204 final float[] value; 205 if (ENABLE_DEVICE_CONFIG) { 206 value = getDeviceConfigFloatArray( 207 key, 208 getSettingsFloatArray(key, defaultValue)); 209 } else { 210 value = getSettingsFloatArray(key, defaultValue); 211 } 212 mCache.put(key, value); 213 return value; 214 } 215 } 216 getSettingsStringList(String key, List<String> defaultValue)217 private List<String> getSettingsStringList(String key, List<String> defaultValue) { 218 return parse(mSettingsParser.getString(key, null), defaultValue); 219 } 220 getDeviceConfigStringList(String key, List<String> defaultValue)221 private static List<String> getDeviceConfigStringList(String key, List<String> defaultValue) { 222 return parse( 223 DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null), 224 defaultValue); 225 } 226 getDeviceConfigFloatArray(String key, float[] defaultValue)227 private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) { 228 return parse( 229 DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null), 230 defaultValue); 231 } 232 getSettingsFloatArray(String key, float[] defaultValue)233 private float[] getSettingsFloatArray(String key, float[] defaultValue) { 234 return parse(mSettingsParser.getString(key, null), defaultValue); 235 } 236 parse(@ullable String listStr, List<String> defaultValue)237 private static List<String> parse(@Nullable String listStr, List<String> defaultValue) { 238 if (listStr != null) { 239 return Collections.unmodifiableList( 240 Arrays.asList(listStr.split(STRING_LIST_DELIMITER))); 241 } 242 return defaultValue; 243 } 244 parse(@ullable String arrayStr, float[] defaultValue)245 private static float[] parse(@Nullable String arrayStr, float[] defaultValue) { 246 if (arrayStr != null) { 247 final String[] split = arrayStr.split(STRING_LIST_DELIMITER); 248 if (split.length != defaultValue.length) { 249 return defaultValue; 250 } 251 final float[] result = new float[split.length]; 252 for (int i = 0; i < split.length; i++) { 253 try { 254 result[i] = Float.parseFloat(split[i]); 255 } catch (NumberFormatException e) { 256 return defaultValue; 257 } 258 } 259 return result; 260 } else { 261 return defaultValue; 262 } 263 } 264 } 265