blob: 63de0590ef7275090806ac854d60ae58c755c049 [file] [log] [blame]
Tony Makfc374572019-03-05 14:46:24 +00001/*
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 */
16package android.view.textclassifier;
17
18import android.annotation.Nullable;
19import android.provider.DeviceConfig;
Abodunrinwa Toki0634af32019-04-04 13:10:59 +010020import android.util.ArrayMap;
Tony Makfc374572019-03-05 14:46:24 +000021import android.util.KeyValueListParser;
22
Abodunrinwa Toki0634af32019-04-04 13:10:59 +010023import com.android.internal.annotations.GuardedBy;
Tony Makfc374572019-03-05 14:46:24 +000024import com.android.internal.annotations.VisibleForTesting;
Abodunrinwa Toki0634af32019-04-04 13:10:59 +010025import com.android.internal.annotations.VisibleForTesting.Visibility;
26import com.android.internal.util.Preconditions;
27
28import java.util.Arrays;
29import java.util.Collections;
30import java.util.List;
31import java.util.Map;
32import java.util.function.Supplier;
Tony Makfc374572019-03-05 14:46:24 +000033
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 */
Abodunrinwa Toki0634af32019-04-04 13:10:59 +010040@VisibleForTesting(visibility = Visibility.PACKAGE)
Tony Makfc374572019-03-05 14:46:24 +000041public final class ConfigParser {
42 private static final String TAG = "ConfigParser";
43
Abodunrinwa Toki0634af32019-04-04 13:10:59 +010044 static final boolean ENABLE_DEVICE_CONFIG = true;
Tony Makfc374572019-03-05 14:46:24 +000045
Abodunrinwa Toki0634af32019-04-04 13:10:59 +010046 private static final String STRING_LIST_DELIMITER = ":";
John Reckbcc0b912019-03-29 14:49:05 -070047
Abodunrinwa Toki0634af32019-04-04 13:10:59 +010048 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
55 public ConfigParser(Supplier<String> legacySettingsSupplier) {
56 mLegacySettingsSupplier = Preconditions.checkNotNull(legacySettingsSupplier);
57 }
58
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;
Tony Makfc374572019-03-05 14:46:24 +000072 }
Tony Makfc374572019-03-05 14:46:24 +000073 }
74
75 /**
Abodunrinwa Toki0634af32019-04-04 13:10:59 +010076 * Reads a boolean setting through the cache.
Tony Makfc374572019-03-05 14:46:24 +000077 */
78 public boolean getBoolean(String key, boolean defaultValue) {
Abodunrinwa Toki0634af32019-04-04 13:10:59 +010079 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;
John Reckbcc0b912019-03-29 14:49:05 -070095 }
Tony Makfc374572019-03-05 14:46:24 +000096 }
97
98 /**
Abodunrinwa Toki0634af32019-04-04 13:10:59 +010099 * Reads an integer setting through the cache.
Tony Makfc374572019-03-05 14:46:24 +0000100 */
101 public int getInt(String key, int defaultValue) {
Abodunrinwa Toki0634af32019-04-04 13:10:59 +0100102 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;
John Reckbcc0b912019-03-29 14:49:05 -0700118 }
Tony Makfc374572019-03-05 14:46:24 +0000119 }
120
121 /**
Abodunrinwa Toki0634af32019-04-04 13:10:59 +0100122 * Reads a float setting through the cache.
Tony Makfc374572019-03-05 14:46:24 +0000123 */
124 public float getFloat(String key, float defaultValue) {
Abodunrinwa Toki0634af32019-04-04 13:10:59 +0100125 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;
John Reckbcc0b912019-03-29 14:49:05 -0700141 }
Tony Makfc374572019-03-05 14:46:24 +0000142 }
143
144 /**
Abodunrinwa Toki0634af32019-04-04 13:10:59 +0100145 * Reads a string setting through the cache.
Tony Makfc374572019-03-05 14:46:24 +0000146 */
147 public String getString(String key, String defaultValue) {
Abodunrinwa Toki0634af32019-04-04 13:10:59 +0100148 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 */
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 */
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
217 private List<String> getSettingsStringList(String key, List<String> defaultValue) {
218 return parse(mSettingsParser.getString(key, null), defaultValue);
219 }
220
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
227 private static float[] getDeviceConfigFloatArray(String key, float[] defaultValue) {
228 return parse(
229 DeviceConfig.getString(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, null),
230 defaultValue);
231 }
232
233 private float[] getSettingsFloatArray(String key, float[] defaultValue) {
234 return parse(mSettingsParser.getString(key, null), defaultValue);
235 }
236
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
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;
John Reckbcc0b912019-03-29 14:49:05 -0700260 } else {
Abodunrinwa Toki0634af32019-04-04 13:10:59 +0100261 return defaultValue;
John Reckbcc0b912019-03-29 14:49:05 -0700262 }
Tony Makfc374572019-03-05 14:46:24 +0000263 }
264}