summaryrefslogtreecommitdiff
path: root/service/java/com/android/server/deviceconfig/DeviceConfigBootstrapValues.java
diff options
context:
space:
mode:
Diffstat (limited to 'service/java/com/android/server/deviceconfig/DeviceConfigBootstrapValues.java')
-rw-r--r--service/java/com/android/server/deviceconfig/DeviceConfigBootstrapValues.java126
1 files changed, 126 insertions, 0 deletions
diff --git a/service/java/com/android/server/deviceconfig/DeviceConfigBootstrapValues.java b/service/java/com/android/server/deviceconfig/DeviceConfigBootstrapValues.java
new file mode 100644
index 0000000..b480bc2
--- /dev/null
+++ b/service/java/com/android/server/deviceconfig/DeviceConfigBootstrapValues.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 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.server.deviceconfig;
+
+import android.annotation.SuppressLint;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+
+/**
+ * @hide
+ */
+public class DeviceConfigBootstrapValues {
+ private static final String TAG = "DeviceConfig";
+ private static final String SYSTEM_OVERRIDES_PATH = "file:///system/etc/device-config-defaults";
+ private static final String META_NAMESPACE = "DeviceConfigBootstrapValues";
+ private static final String META_KEY = "processed_values";
+
+ private final String defaultValuesPath;
+
+ public DeviceConfigBootstrapValues() {
+ this(SYSTEM_OVERRIDES_PATH);
+ }
+
+ public DeviceConfigBootstrapValues(String defaultValuesPath) {
+ this.defaultValuesPath = defaultValuesPath;
+ }
+
+ /**
+ * Performs the logic to apply bootstrap values when needed.
+ *
+ * If a file with the bootstrap values exists and they haven't been parsed before,
+ * it will parse the file and apply the values.
+ *
+ * @throws IOException if there's a problem reading the bootstrap file
+ * @throws RuntimeException if setting the values in DeviceConfig throws an exception
+ */
+ public void applyValuesIfNeeded() throws IOException {
+ if (getPath().toFile().exists()) {
+ if (checkIfHasAlreadyParsedBootstrapValues()) {
+ Slog.i(TAG, "Bootstrap values already parsed, not processing again");
+ } else {
+ parseAndApplyBootstrapValues();
+ Slog.i(TAG, "Parsed bootstrap values");
+ }
+ } else {
+ Slog.i(TAG, "Bootstrap values not found");
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ private boolean checkIfHasAlreadyParsedBootstrapValues() {
+ DeviceConfig.Properties properties = DeviceConfig.getProperties(META_NAMESPACE);
+ return properties.getKeyset().size() > 0;
+ }
+
+ @SuppressLint("MissingPermission")
+ private void parseAndApplyBootstrapValues() throws IOException {
+ Path path = getPath();
+ try (Stream<String> lines = Files.lines(path)) {
+ lines.forEach(line -> processLine(line));
+ }
+ // store a property in DeviceConfig so that we know we have successufully
+ // processed this
+ writeToDeviceConfig(META_NAMESPACE, META_KEY, "true");
+ }
+
+ private void processLine(String line) {
+ // contents for each line:
+ // <namespace>:<package>.<flag-name>=[enabled|disabled]
+ // we actually use <package>.<flag-name> combined in calls into DeviceConfig
+ int namespaceDelimiter = line.indexOf(':');
+ String namespace = line.substring(0, namespaceDelimiter);
+ if (namespaceDelimiter < 1) {
+ throw new IllegalArgumentException("Unexpectedly found : at index "
+ + namespaceDelimiter);
+ }
+ int valueDelimiter = line.indexOf('=');
+ if (valueDelimiter < 5) {
+ throw new IllegalArgumentException("Unexpectedly found = at index " + valueDelimiter);
+ }
+ String key = line.substring(namespaceDelimiter + 1, valueDelimiter);
+ String value = line.substring(valueDelimiter + 1);
+ String val;
+ if ("enabled".equals(value)) {
+ val = "true";
+ } else if ("disabled".equals(value)) {
+ val = "false";
+ } else {
+ throw new IllegalArgumentException("Received unexpected value: " + value);
+ }
+ writeToDeviceConfig(namespace, key, val);
+ }
+
+ @SuppressLint("MissingPermission")
+ private void writeToDeviceConfig(String namespace, String key, String value) {
+ boolean result = DeviceConfig.setProperty(namespace, key, value, /* makeDefault= */ true);
+ if (!result) {
+ throw new RuntimeException("Failed to set DeviceConfig property [" + namespace + "] "
+ + key + "=" + value);
+ }
+ }
+
+ private Path getPath() {
+ return Path.of(URI.create(defaultValuesPath));
+ }
+}