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

Commit f2f8ed8b authored by William Escande's avatar William Escande
Browse files

SystemServer: reformat flag dumpsys

Clearly indicate that the flag is subject to a local override.
Clearly indicate if the override as been wrongly done
Modify format to only display snake_case name.

Bellow showing different status of flags:

unactif flag
[ ]: foo_flag
actif flag
[■]: foo_flag
actif flag when detecting a local override
[■]: foo_flag  (Manual override)
unactif flag because incorrect overrid (java only)
[ ]: foo_flag  (Manual override) ([■]: Java) ([ ]: Native) INCONSISTENT OVERRIDE FOR THIS FLAG. This can lead to unpredictable behavior.
unactif flag because incorrect overrid (native only)
[ ]: foo_flag  (Manual override) ([ ]: Java) ([■]: Native) INCONSISTENT OVERRIDE FOR THIS FLAG. This can lead to unpredictable behavior.
unactif flag because incorrect overrid (java & native without system reboot)
[ ]: foo_flag  (Manual override) ([■]: Java) ([■]: Native) INCONSISTENT OVERRIDE FOR THIS FLAG. This can lead to unpredictable behavior.

FYI this is how flags were currently printed:
[ ]: fooFlag   foo_flag
[■]: barFlag   bar_flag

Bug: 311772251
Test: manual Build apex, print the flags and check how pretty it is
Flag: Exempt dump mechanisme for flags
Change-Id: I013cebd6fb48bc78cc880e7b79360d4579289bb7
parent 206f009e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ java_defaults {
            "-Xep:EmptyCatch:ERROR",
            "-Xep:EqualsIncompatibleType:ERROR",
            "-Xep:FutureReturnValueIgnored:ERROR",
            "-Xep:InlineFormatString:ERROR",
            "-Xep:InlineMeInliner:ERROR",
            "-Xep:InvalidBlockTag:ERROR",
            "-Xep:InvalidInlineTag:ERROR",
+96 −22
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.sysprop.BluetoothProperties;
@@ -116,6 +117,7 @@ import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

class BluetoothManagerService {
    private static final String TAG = BluetoothManagerService.class.getSimpleName();
@@ -2327,6 +2329,13 @@ class BluetoothManagerService {
        }
        String errorMsg = null;

        String flagString = "";
        try {
            flagString = dumpBluetoothFlags(writer);
        } catch (Exception e) {
            writer.println("Exception while dumping Bluetooth Flags");
        }

        writer.println("Bluetooth Status");
        writer.println("  enabled: " + isEnabled());
        writer.println("  state: " + mState);
@@ -2382,11 +2391,7 @@ class BluetoothManagerService {
            args[0] = "--print";
        }

        try {
            dumpBluetoothFlags(writer);
        } catch (Exception e) {
            writer.println("Exception while dumping Bluetooth Flags");
        }
        writer.println(flagString);

        if (mAdapter == null) {
            errorMsg = "Bluetooth Service not connected";
@@ -2402,28 +2407,97 @@ class BluetoothManagerService {
        }
    }

    private void dumpBluetoothFlags(PrintWriter writer)
            throws IllegalAccessException, InvocationTargetException {
        writer.println("🚩Flag dump:");
    private static class FlagValue {
        private final String mSnakeName;
        private final boolean mDefaultValue;
        private final boolean mManuallyEnabledInJava;
        private final boolean mManuallyEnabledInNative;

        // maxLen is used to align the flag output
        int maxLen =
                Arrays.stream(Flags.class.getDeclaredMethods())
                        .map(Method::getName)
                        .map(String::length)
                        .max(Integer::compare)
                        .get();
        FlagValue(String name, boolean defaultValue) {
            mSnakeName = name.replaceAll("([A-Z])", "_$1").toLowerCase(Locale.US);
            mDefaultValue = defaultValue;
            mManuallyEnabledInJava = getJavaFlagValue();
            mManuallyEnabledInNative = getNativeFlagValue();
        }

        private boolean getJavaFlagValue() {
            return DeviceConfig.getBoolean(
                    DeviceConfig.NAMESPACE_BLUETOOTH,
                    "com.android.bluetooth.flags." + mSnakeName,
                    false);
        }

        private boolean getNativeFlagValue() {
            return SystemProperties.getBoolean(
                    "persist.device_config.aconfig_flags.bluetooth.com.android.bluetooth.flags."
                            + mSnakeName,
                    false);
        }

        String fmt = "\t%s: %-" + maxLen + "s %s";
        boolean isManuallyOverride() {
            return mManuallyEnabledInJava || mManuallyEnabledInNative;
        }

        boolean isPartiallyOverride() {
            return isManuallyOverride()
                    && (mDefaultValue != mManuallyEnabledInJava
                            || mDefaultValue != mManuallyEnabledInNative
                            || mManuallyEnabledInJava != mManuallyEnabledInNative);
        }

        static String toIcon(boolean flagValue) {
            return flagValue ? "[■]" : "[ ]";
        }

        for (Method m : Flags.class.getDeclaredMethods()) {
            String flagStatus = ((Boolean) m.invoke(null)) ? "[■]" : "[ ]";
            String name = m.getName();
            String snakeCaseName = name.replaceAll("([A-Z])", "_$1").toLowerCase(Locale.US);
            writer.println(String.format(fmt, flagStatus, name, snakeCaseName));
        void dump(StringBuilder sb) {
            sb.append("\t").append(toIcon(mDefaultValue)).append(": ").append(mSnakeName);
            if (isManuallyOverride()) {
                sb.append(" (Manual override)");
                if (isPartiallyOverride()) {
                    sb.append(String.format(" (%s: Java)", toIcon(mManuallyEnabledInJava)));
                    sb.append(String.format(" (%s: Native)", toIcon(mManuallyEnabledInNative)));
                    sb.append(" INCONSISTENT OVERRIDE FOR THIS FLAG.")
                            .append(" This can lead to unpredictable behavior.");
                }
            }
            sb.append("\n");
        }
    }

    private String dumpBluetoothFlags(PrintWriter writer) {
        List<FlagValue> flags =
                Arrays.stream(Flags.class.getDeclaredMethods())
                        .map(
                                (Method m) -> {
                                    try {
                                        return new FlagValue(m.getName(), (boolean) m.invoke(null));
                                    } catch (IllegalAccessException | InvocationTargetException e) {
                                        writer.println("Exception caught while dumping flag:" + e);
                                        throw new RuntimeException(e);
                                    }
                                })
                        .collect(Collectors.toList());

        StringBuilder flagOverride = new StringBuilder();
        flags.stream().filter(FlagValue::isManuallyOverride).forEach(f -> f.dump(flagOverride));
        if (flagOverride.length() > 0) {
            writer.println("🚩Some flag have a local override. Make sure this is expected");
            if (flags.stream().anyMatch(FlagValue::isPartiallyOverride)) {
                writer.println("CRITICAL WARNING:");
                writer.println("\tSome flags differ between native and java code !");
                writer.println(
                        "\tEither they are only enabled in java or only enabled in native. This can"
                                + " lead to critical failure and/or hard to debug issues.");
            }

            writer.println(flagOverride.toString());
            writer.println("---------------------------------------------------------------------");
            writer.println("");
        }
        StringBuilder flagDumpBuilder = new StringBuilder("🚩Flag dump:\n");
        flags.stream().forEach(f -> f.dump(flagDumpBuilder));
        return flagDumpBuilder.toString();
    }

    private void dumpProto(FileDescriptor fd) {
        final ProtoOutputStream proto = new ProtoOutputStream(new FileOutputStream(fd));