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

Commit f1a5f74f authored by Dave Mankoff's avatar Dave Mankoff
Browse files

Use less SysUI specific code in Flags.

A step along they way to sharing the flagging code as a library.

Bug: 249121873
Test: atest SystemUITests
Change-Id: Id48d33a6ce0d058c4b2d3c282865297841753bb7
parent 81389920
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.flags

import android.content.Context
import android.os.Handler
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsDebug.ALL_FLAGS
import com.android.systemui.util.settings.SettingsUtilModule
@@ -27,6 +28,7 @@ import dagger.Provides
import javax.inject.Named

@Module(includes = [
    FeatureFlagsDebugStartableModule::class,
    ServerFlagReaderModule::class,
    SettingsUtilModule::class,
])
@@ -46,5 +48,15 @@ abstract class FlagsModule {
        @Provides
        @Named(ALL_FLAGS)
        fun providesAllFlags(): Map<Int, Flag<*>> = Flags.collectFlags()

        @JvmStatic
        @Provides
        fun providesRestarter(barService: IStatusBarService): Restarter {
            return object: Restarter {
                override fun restart() {
                    barService.restart()
                }
            }
        }
    }
}
+19 −1
Original line number Diff line number Diff line
@@ -16,11 +16,29 @@

package com.android.systemui.flags

import com.android.internal.statusbar.IStatusBarService
import dagger.Binds
import dagger.Module
import dagger.Provides

@Module(includes = [ServerFlagReaderModule::class])
@Module(includes = [
    FeatureFlagsReleaseStartableModule::class,
    ServerFlagReaderModule::class
])
abstract class FlagsModule {
    @Binds
    abstract fun bindsFeatureFlagRelease(impl: FeatureFlagsRelease): FeatureFlags

    @Module
    companion object {
        @JvmStatic
        @Provides
        fun providesRestarter(barService: IStatusBarService): Restarter {
            return object: Restarter {
                override fun restart() {
                    barService.restart()
                }
            }
        }
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -16,12 +16,14 @@

package com.android.systemui.flags

import android.util.Dumpable

/**
 * Class to manage simple DeviceConfig-based feature flags.
 *
 * See [Flags] for instructions on defining new flags.
 */
interface FeatureFlags : FlagListenable {
interface FeatureFlags : FlagListenable, Dumpable {
    /** Returns a boolean value for the given flag.  */
    fun isEnabled(flag: UnreleasedFlag): Boolean

+9 −175
Original line number Diff line number Diff line
@@ -30,29 +30,21 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.commandline.Command;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.settings.SecureSettings;

import org.jetbrains.annotations.NotNull;

import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
@@ -75,10 +67,9 @@ import javax.inject.Named;
 * To restore a flag back to its default, leave the `--ez value <0|1>` off of the command.
 */
@SysUISingleton
public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
    private static final String TAG = "SysUIFlags";
public class FeatureFlagsDebug implements FeatureFlags {
    static final String TAG = "SysUIFlags";
    static final String ALL_FLAGS = "all_flags";
    private static final String FLAG_COMMAND = "flag";

    private final FlagManager mFlagManager;
    private final SecureSettings mSecureSettings;
@@ -89,7 +80,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
    private final Map<Integer, Flag<?>> mAllFlags;
    private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>();
    private final Map<Integer, String> mStringFlagCache = new TreeMap<>();
    private final IStatusBarService mBarService;
    private final Restarter mRestarter;

    @Inject
    public FeatureFlagsDebug(
@@ -98,12 +89,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
            SecureSettings secureSettings,
            SystemPropertiesHelper systemProperties,
            @Main Resources resources,
            DumpManager dumpManager,
            DeviceConfigProxy deviceConfigProxy,
            ServerFlagReader serverFlagReader,
            @Named(ALL_FLAGS) Map<Integer, Flag<?>> allFlags,
            CommandRegistry commandRegistry,
            IStatusBarService barService) {
            Restarter barService) {
        mFlagManager = flagManager;
        mSecureSettings = secureSettings;
        mResources = resources;
@@ -111,7 +100,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
        mDeviceConfigProxy = deviceConfigProxy;
        mServerFlagReader = serverFlagReader;
        mAllFlags = allFlags;
        mBarService = barService;
        mRestarter = barService;

        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_SET_FLAG);
@@ -120,8 +109,6 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
        flagManager.setClearCacheAction(this::removeFromCache);
        context.registerReceiver(mReceiver, filter, null, null,
                Context.RECEIVER_EXPORTED_UNAUDITED);
        dumpManager.registerDumpable(TAG, this);
        commandRegistry.registerCommand(FLAG_COMMAND, FlagCommand::new);
    }

    @Override
@@ -266,7 +253,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
        mFlagManager.dispatchListenersAndMaybeRestart(id, this::restartSystemUI);
    }

    private <T> void eraseFlag(Flag<T> flag) {
    <T> void eraseFlag(Flag<T> flag) {
        if (flag instanceof SysPropFlag) {
            mSystemProperties.erase(((SysPropFlag<T>) flag).getName());
            dispatchListenersAndMaybeRestart(flag.getId(), this::restartAndroid);
@@ -319,13 +306,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
            return;
        }
        Log.i(TAG, "Restarting Android");
        try {
            mBarService.restart();
        } catch (RemoteException e) {
        }
        mRestarter.restart();
    }

    private void setBooleanFlagInternal(Flag<?> flag, boolean value) {
    void setBooleanFlagInternal(Flag<?> flag, boolean value) {
        if (flag instanceof BooleanFlag) {
            setFlagValue(flag.getId(), value, BooleanFlagSerializer.INSTANCE);
        } else if (flag instanceof ResourceBooleanFlag) {
@@ -342,7 +326,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
        }
    }

    private void setStringFlagInternal(Flag<?> flag, String value) {
    void setStringFlagInternal(Flag<?> flag, String value) {
        if (flag instanceof StringFlag) {
            setFlagValue(flag.getId(), value, StringFlagSerializer.INSTANCE);
        } else if (flag instanceof ResourceStringFlag) {
@@ -476,154 +460,4 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable {
                + ": [length=" + value.length() + "] \"" + value + "\""));
    }

    class FlagCommand implements Command {
        private final List<String> mOnCommands = List.of("true", "on", "1", "enabled");
        private final List<String> mOffCommands = List.of("false", "off", "0", "disable");

        @Override
        public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) {
            if (args.size() == 0) {
                pw.println("Error: no flag id supplied");
                help(pw);
                pw.println();
                printKnownFlags(pw);
                return;
            }

            if (args.size() > 2) {
                pw.println("Invalid number of arguments.");
                help(pw);
                return;
            }

            int id = 0;
            try {
                id = Integer.parseInt(args.get(0));
                if (!mAllFlags.containsKey(id)) {
                    pw.println("Unknown flag id: " + id);
                    pw.println();
                    printKnownFlags(pw);
                    return;
                }
            } catch (NumberFormatException e) {
                id = flagNameToId(args.get(0));
                if (id == 0) {
                    pw.println("Invalid flag. Must an integer id or flag name: " + args.get(0));
                    return;
                }
            }
            Flag<?> flag = mAllFlags.get(id);

            String cmd = "";
            if (args.size() == 2) {
                cmd = args.get(1).toLowerCase();
            }

            if ("erase".equals(cmd) || "reset".equals(cmd)) {
                eraseFlag(flag);
                return;
            }

            boolean newValue = true;
            if (args.size() == 1 || "toggle".equals(cmd)) {
                boolean enabled = isBooleanFlagEnabled(flag);

                if (args.size() == 1) {
                    pw.println("Flag " + id + " is " + enabled);
                    return;
                }

                newValue = !enabled;
            } else {
                newValue = mOnCommands.contains(cmd);
                if (!newValue && !mOffCommands.contains(cmd)) {
                    pw.println("Invalid on/off argument supplied");
                    help(pw);
                    return;
                }
            }

            pw.flush();  // Next command will restart sysui, so flush before we do so.
            setBooleanFlagInternal(flag, newValue);
        }

        @Override
        public void help(PrintWriter pw) {
            pw.println(
                    "Usage: adb shell cmd statusbar flag <id> "
                            + "[true|false|1|0|on|off|enable|disable|toggle|erase|reset]");
            pw.println("The id can either be a numeric integer or the corresponding field name");
            pw.println(
                    "If no argument is supplied after the id, the flags runtime value is output");
        }

        private boolean isBooleanFlagEnabled(Flag<?> flag) {
            if (flag instanceof ReleasedFlag) {
                return isEnabled((ReleasedFlag) flag);
            } else if (flag instanceof UnreleasedFlag) {
                return isEnabled((UnreleasedFlag) flag);
            } else if (flag instanceof ResourceBooleanFlag) {
                return isEnabled((ResourceBooleanFlag) flag);
            } else if (flag instanceof SysPropFlag) {
                return isEnabled((SysPropBooleanFlag) flag);
            }

            return false;
        }

        private int flagNameToId(String flagName) {
            List<Field> fields = Flags.getFlagFields();
            for (Field field : fields) {
                if (flagName.equals(field.getName())) {
                    return fieldToId(field);
                }
            }

            return 0;
        }

        private int fieldToId(Field field) {
            try {
                Flag<?> flag = (Flag<?>) field.get(null);
                return flag.getId();
            } catch (IllegalAccessException e) {
                // no-op
            }

            return 0;
        }

        private void printKnownFlags(PrintWriter pw) {
            List<Field> fields = Flags.getFlagFields();

            int longestFieldName = 0;
            for (Field field : fields) {
                longestFieldName = Math.max(longestFieldName, field.getName().length());
            }

            pw.println("Known Flags:");
            pw.print("Flag Name");
            for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) {
                pw.print(" ");
            }
            pw.println("ID   Enabled?");
            for (int i = 0; i < longestFieldName; i++) {
                pw.print("=");
            }
            pw.println(" ==== ========");
            for (Field field : fields) {
                int id = fieldToId(field);
                if (id == 0 || !mAllFlags.containsKey(id)) {
                    continue;
                }
                pw.print(field.getName());
                int fieldWidth = field.getName().length();
                for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) {
                    pw.print(" ");
                }
                pw.printf("%-4d ", id);
                pw.println(isBooleanFlagEnabled(mAllFlags.get(id)));
            }
        }
    }
}
+57 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.systemui.flags

import android.content.Context
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.commandline.CommandRegistry
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Inject

class FeatureFlagsDebugStartable
@Inject
constructor(
    @Application context: Context,
    dumpManager: DumpManager,
    private val commandRegistry: CommandRegistry,
    private val flagCommand: FlagCommand,
    featureFlags: FeatureFlags
) : CoreStartable(context) {

    init {
        dumpManager.registerDumpable(FeatureFlagsDebug.TAG) { pw, args ->
            featureFlags.dump(pw, args)
        }
    }

    override fun start() {
        commandRegistry.registerCommand(FlagCommand.FLAG_COMMAND) { flagCommand }
    }
}

@Module
abstract class FeatureFlagsDebugStartableModule {
    @Binds
    @IntoMap
    @ClassKey(FeatureFlagsDebugStartable::class)
    abstract fun bind(impl: FeatureFlagsDebugStartable): CoreStartable
}
Loading