Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef615889 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "For aconfig flag writes, check write permission" into main

parents 1bfd402c 4b7c2bb6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ android_library {
        "unsupportedappusage",
    ],
    static_libs: [
        "aconfig_device_paths_java",
        "aconfig_new_storage_flags_lib",
        "aconfigd_java_utils",
        "aconfig_demo_flags_java_lib",
+108 −14
Original line number Diff line number Diff line
@@ -20,8 +20,10 @@ import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE;
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.Settings.Config.SyncDisabledMode;
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 @@ public final class DeviceConfigService extends Binder {
                    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 @@ public final class DeviceConfigService extends Binder {

    static final class MyShellCommand extends ShellCommand {
        final SettingsProvider mProvider;
        private HashMap<String, parsed_flag> mAconfigParsedFlags;

        enum CommandVerb {
            GET,
@@ -186,6 +185,51 @@ public final class DeviceConfigService extends Binder {

        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 @@ public final class DeviceConfigService extends Binder {
                    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);
+11 −0
Original line number Diff line number Diff line
@@ -70,3 +70,14 @@ flag {
        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
    }
}