For aconfig flag writes, check write permission
Flag: com.android.providers.settings.check_root_and_read_only
Bug: 342636474
Test: m
Change-Id: I422b076bf8e44bbaa1185822ef99dbb4ff588084
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 75f8384..3e62b7b 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -32,6 +32,7 @@
"unsupportedappusage",
],
static_libs: [
+ "aconfig_device_paths_java",
"aconfig_new_storage_flags_lib",
"aconfigd_java_utils",
"aconfig_demo_flags_java_lib",
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 8b0772b..121bd3e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -20,8 +20,10 @@
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT;
import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT;
-import android.aconfig.Aconfig.parsed_flag;
-import android.aconfig.Aconfig.parsed_flags;
+import android.aconfig.DeviceProtos;
+import android.aconfig.nano.Aconfig;
+import android.aconfig.nano.Aconfig.parsed_flag;
+import android.aconfig.nano.Aconfig.parsed_flags;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.AttributionSource;
@@ -42,7 +44,6 @@
import android.provider.UpdatableDeviceConfigServiceReadiness;
import android.util.Slog;
-import com.android.internal.pm.pkg.component.AconfigFlags;
import com.android.internal.util.FastPrintWriter;
import java.io.File;
@@ -136,11 +137,8 @@
continue;
}
- for (parsed_flag flag : parsedFlags.getParsedFlagList()) {
- String namespace = flag.getNamespace();
- String packageName = flag.getPackage();
- String name = flag.getName();
- nameSet.add(namespace + "/" + packageName + "." + name);
+ for (parsed_flag flag : parsedFlags.parsedFlag) {
+ nameSet.add(flag.namespace + "/" + flag.package_ + "." + flag.name);
}
}
} catch (IOException e) {
@@ -169,6 +167,7 @@
static final class MyShellCommand extends ShellCommand {
final SettingsProvider mProvider;
+ private HashMap<String, parsed_flag> mAconfigParsedFlags;
enum CommandVerb {
GET,
@@ -186,6 +185,51 @@
MyShellCommand(SettingsProvider provider) {
mProvider = provider;
+
+ if (Flags.checkRootAndReadOnly()) {
+ List<parsed_flag> parsedFlags;
+ try {
+ parsedFlags = DeviceProtos.loadAndParseFlagProtos();
+ } catch (IOException e) {
+ throw new IllegalStateException("failed to parse aconfig protos");
+ }
+
+ mAconfigParsedFlags = new HashMap();
+ for (parsed_flag flag : parsedFlags) {
+ mAconfigParsedFlags.put(flag.package_ + "." + flag.name, flag);
+ }
+ }
+ }
+
+ /**
+ * Return true if a flag is aconfig.
+ */
+ private boolean isAconfigFlag(String name) {
+ return mAconfigParsedFlags.get(name) != null;
+ }
+
+ /**
+ * Return true if a flag is both aconfig and read-only.
+ *
+ * @return true if a flag is both aconfig and read-only
+ */
+ private boolean isReadOnly(String name) {
+ parsed_flag flag = mAconfigParsedFlags.get(name);
+ if (flag != null) {
+ if (flag.permission == Aconfig.READ_ONLY) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return true if the calling process is root.
+ *
+ * @return true if a flag is aconfig, and the calling process is root
+ */
+ private boolean isRoot() {
+ return Binder.getCallingUid() == Process.ROOT_UID;
}
public static HashMap<String, String> getAllFlags(IContentProvider provider) {
@@ -414,21 +458,71 @@
pout.println(DeviceConfig.getProperty(namespace, key));
break;
case PUT:
+ if (Flags.checkRootAndReadOnly()) {
+ if (isAconfigFlag(key)) {
+ if (!isRoot()) {
+ pout.println("Error: must be root to write aconfig flag");
+ break;
+ }
+
+ if (isReadOnly(key)) {
+ pout.println("Error: cannot write read-only flag");
+ break;
+ }
+ }
+ }
+
DeviceConfig.setProperty(namespace, key, value, makeDefault);
break;
case OVERRIDE:
- AconfigFlags.Permission permission =
- (new AconfigFlags()).getFlagPermission(key);
- if (permission == AconfigFlags.Permission.READ_ONLY) {
- pout.println("cannot override read-only flag " + key);
- } else {
- DeviceConfig.setLocalOverride(namespace, key, value);
+ if (Flags.checkRootAndReadOnly()) {
+ if (isAconfigFlag(key)) {
+ if (!isRoot()) {
+ pout.println("Error: must be root to write aconfig flag");
+ break;
+ }
+
+ if (isReadOnly(key)) {
+ pout.println("Error: cannot write read-only flag");
+ break;
+ }
+ }
}
+
+ DeviceConfig.setLocalOverride(namespace, key, value);
break;
case CLEAR_OVERRIDE:
+ if (Flags.checkRootAndReadOnly()) {
+ if (isAconfigFlag(key)) {
+ if (!isRoot()) {
+ pout.println("Error: must be root to write aconfig flag");
+ break;
+ }
+
+ if (isReadOnly(key)) {
+ pout.println("Error: cannot write read-only flag");
+ break;
+ }
+ }
+ }
+
DeviceConfig.clearLocalOverride(namespace, key);
break;
case DELETE:
+ if (Flags.checkRootAndReadOnly()) {
+ if (isAconfigFlag(key)) {
+ if (!isRoot()) {
+ pout.println("Error: must be root to write aconfig flag");
+ break;
+ }
+
+ if (isReadOnly(key)) {
+ pout.println("Error: cannot write read-only flag");
+ break;
+ }
+ }
+ }
+
pout.println(delete(iprovider, namespace, key)
? "Successfully deleted " + key + " from " + namespace
: "Failed to delete " + key + " from " + namespace);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index b1e6d66..006e644 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -70,3 +70,14 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "check_root_and_read_only"
+ namespace: "core_experiments_team_internal"
+ description: "Check root and aconfig flag permissions in adb shell device_config commands."
+ bug: "342636474"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}