diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt index 74bd9c6c287df..bb3df8f0358a2 100644 --- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt +++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt @@ -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> = Flags.collectFlags() + + @JvmStatic + @Provides + fun providesRestarter(barService: IStatusBarService): Restarter { + return object: Restarter { + override fun restart() { + barService.restart() + } + } + } } } diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt index 38b5c9a9fa794..0f7e732fceb1b 100644 --- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt +++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt @@ -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() + } + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt index dfa3bcda7d725..fb4fc928c8dc4 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java index 00c1a99983df7..b983e5c0a0f70 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java @@ -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> mAllFlags; private final Map mBooleanFlagCache = new TreeMap<>(); private final Map 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> 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 void eraseFlag(Flag flag) { + void eraseFlag(Flag flag) { if (flag instanceof SysPropFlag) { mSystemProperties.erase(((SysPropFlag) 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 mOnCommands = List.of("true", "on", "1", "enabled"); - private final List mOffCommands = List.of("false", "off", "0", "disable"); - - @Override - public void execute(@NonNull PrintWriter pw, @NonNull List 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 " - + "[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 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 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))); - } - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt new file mode 100644 index 0000000000000..c0e30211e018d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugStartable.kt @@ -0,0 +1,57 @@ +/* + * 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 +} diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java index 049b17d383a27..40a8a1a9ef012 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java @@ -24,10 +24,8 @@ import android.util.SparseBooleanArray; import androidx.annotation.NonNull; -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.util.DeviceConfigProxy; import org.jetbrains.annotations.NotNull; @@ -44,27 +42,26 @@ import javax.inject.Inject; * how to set flags. */ @SysUISingleton -public class FeatureFlagsRelease implements FeatureFlags, Dumpable { +public class FeatureFlagsRelease implements FeatureFlags { + static final String TAG = "SysUIFlags"; + private final Resources mResources; private final SystemPropertiesHelper mSystemProperties; private final DeviceConfigProxy mDeviceConfigProxy; private final ServerFlagReader mServerFlagReader; SparseBooleanArray mBooleanCache = new SparseBooleanArray(); SparseArray mStringCache = new SparseArray<>(); - private boolean mInited; @Inject public FeatureFlagsRelease( @Main Resources resources, SystemPropertiesHelper systemProperties, DeviceConfigProxy deviceConfigProxy, - ServerFlagReader serverFlagReader, - DumpManager dumpManager) { + ServerFlagReader serverFlagReader) { mResources = resources; mSystemProperties = systemProperties; mDeviceConfigProxy = deviceConfigProxy; mServerFlagReader = serverFlagReader; - dumpManager.registerDumpable("SysUIFlags", this); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt new file mode 100644 index 0000000000000..f138f1e8aa798 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseStartable.kt @@ -0,0 +1,51 @@ +/* + * 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 dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap +import javax.inject.Inject + +class FeatureFlagsReleaseStartable +@Inject +constructor(@Application context: Context, dumpManager: DumpManager, featureFlags: FeatureFlags) : + CoreStartable(context) { + + init { + dumpManager.registerDumpable(FeatureFlagsRelease.TAG) { pw, args -> + featureFlags.dump(pw, args) + } + } + + override fun start() { + // no-op + } +} + +@Module +abstract class FeatureFlagsReleaseStartableModule { + @Binds + @IntoMap + @ClassKey(FeatureFlagsReleaseStartable::class) + abstract fun bind(impl: FeatureFlagsReleaseStartable): CoreStartable +} diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java new file mode 100644 index 0000000000000..4d254313a57b9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagCommand.java @@ -0,0 +1,196 @@ +/* + * 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 androidx.annotation.NonNull; + +import com.android.systemui.statusbar.commandline.Command; + +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * A {@link Command} used to flip flags in SystemUI. + */ +public class FlagCommand implements Command { + public static final String FLAG_COMMAND = "flag"; + + private final List mOnCommands = List.of("true", "on", "1", "enabled"); + private final List mOffCommands = List.of("false", "off", "0", "disable"); + private final FeatureFlagsDebug mFeatureFlags; + private final Map> mAllFlags; + + @Inject + FlagCommand( + FeatureFlagsDebug featureFlags, + @Named(FeatureFlagsDebug.ALL_FLAGS) Map> allFlags + ) { + mFeatureFlags = featureFlags; + mAllFlags = allFlags; + } + + @Override + public void execute(@NonNull PrintWriter pw, @NonNull List 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)) { + mFeatureFlags.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. + mFeatureFlags.setBooleanFlagInternal(flag, newValue); + } + + @Override + public void help(PrintWriter pw) { + pw.println( + "Usage: adb shell cmd statusbar flag " + + "[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 mFeatureFlags.isEnabled((ReleasedFlag) flag); + } else if (flag instanceof UnreleasedFlag) { + return mFeatureFlags.isEnabled((UnreleasedFlag) flag); + } else if (flag instanceof ResourceBooleanFlag) { + return mFeatureFlags.isEnabled((ResourceBooleanFlag) flag); + } else if (flag instanceof SysPropFlag) { + return mFeatureFlags.isEnabled((SysPropBooleanFlag) flag); + } + + return false; + } + + private int flagNameToId(String flagName) { + List 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 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))); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt b/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt new file mode 100644 index 0000000000000..8f095a24de94f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt @@ -0,0 +1,20 @@ +/* + * 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 + +interface Restarter { + fun restart() +} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt index 4511193d41d3a..20a82c63cfdd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt @@ -21,15 +21,10 @@ import android.content.Intent import android.content.pm.PackageManager.NameNotFoundException import android.content.res.Resources import android.test.suitebuilder.annotation.SmallTest -import com.android.internal.statusbar.IStatusBarService import com.android.systemui.SysuiTestCase -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.DeviceConfigProxyFake import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.argumentCaptor -import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.android.systemui.util.mockito.withArgCaptor @@ -46,18 +41,16 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.anyString -import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.inOrder import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions -import org.mockito.Mockito.verifyZeroInteractions import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations /** - * NOTE: This test is for the version of FeatureFlagManager in src-release, which should not allow - * overriding, and should never return any value other than the one provided as the default. + * NOTE: This test is for the version of FeatureFlagManager in src-debug, which allows overriding + * the default. */ @SmallTest class FeatureFlagsDebugTest : SysuiTestCase() { @@ -68,10 +61,8 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Mock private lateinit var secureSettings: SecureSettings @Mock private lateinit var systemProperties: SystemPropertiesHelper @Mock private lateinit var resources: Resources - @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var commandRegistry: CommandRegistry - @Mock private lateinit var barService: IStatusBarService - @Mock private lateinit var pw: PrintWriter + @Mock private lateinit var restarter: Restarter private val flagMap = mutableMapOf>() private lateinit var broadcastReceiver: BroadcastReceiver private lateinit var clearCacheAction: Consumer @@ -92,12 +83,10 @@ class FeatureFlagsDebugTest : SysuiTestCase() { secureSettings, systemProperties, resources, - dumpManager, deviceConfig, serverFlagReader, flagMap, - commandRegistry, - barService + restarter ) verify(flagManager).onSettingsChangedAction = any() broadcastReceiver = withArgCaptor { @@ -365,53 +354,6 @@ class FeatureFlagsDebugTest : SysuiTestCase() { assertThat(mFeatureFlagsDebug.isEnabled(flag)).isTrue() } - @Test - fun statusBarCommand_IsRegistered() { - verify(commandRegistry).registerCommand(anyString(), any()) - } - - @Test - fun noOpCommand() { - val cmd = captureCommand() - - cmd.execute(pw, ArrayList()) - verify(pw, atLeastOnce()).println() - verify(flagManager).readFlagValue(eq(1), any()) - verifyZeroInteractions(secureSettings) - } - - @Test - fun readFlagCommand() { - addFlag(UnreleasedFlag(1)) - val cmd = captureCommand() - cmd.execute(pw, listOf("1")) - verify(flagManager).readFlagValue(eq(1), any()) - } - - @Test - fun setFlagCommand() { - addFlag(UnreleasedFlag(1)) - val cmd = captureCommand() - cmd.execute(pw, listOf("1", "on")) - verifyPutData(1, "{\"type\":\"boolean\",\"value\":true}") - } - - @Test - fun toggleFlagCommand() { - addFlag(ReleasedFlag(1)) - val cmd = captureCommand() - cmd.execute(pw, listOf("1", "toggle")) - verifyPutData(1, "{\"type\":\"boolean\",\"value\":false}", 2) - } - - @Test - fun eraseFlagCommand() { - addFlag(ReleasedFlag(1)) - val cmd = captureCommand() - cmd.execute(pw, listOf("1", "erase")) - verify(secureSettings).putStringForUser(eq("key-1"), eq(""), anyInt()) - } - @Test fun dumpFormat() { val flag1 = ReleasedFlag(1) @@ -471,13 +413,6 @@ class FeatureFlagsDebugTest : SysuiTestCase() { return flag } - private fun captureCommand(): Command { - val captor = argumentCaptor>() - verify(commandRegistry).registerCommand(anyString(), capture(captor)) - - return captor.value.invoke() - } - private fun dumpToString(): String { val sw = StringWriter() val pw = PrintWriter(sw) diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt index e94b5202956dd..575c14262b742 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt @@ -19,17 +19,12 @@ import android.content.pm.PackageManager.NameNotFoundException import android.content.res.Resources import android.test.suitebuilder.annotation.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.dump.DumpManager import com.android.systemui.util.DeviceConfigProxyFake -import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat -import org.junit.After import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Test import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @@ -43,7 +38,6 @@ class FeatureFlagsReleaseTest : SysuiTestCase() { @Mock private lateinit var mResources: Resources @Mock private lateinit var mSystemProperties: SystemPropertiesHelper - @Mock private lateinit var mDumpManager: DumpManager private val serverFlagReader = ServerFlagReaderFake() private val deviceConfig = DeviceConfigProxyFake() @@ -55,15 +49,7 @@ class FeatureFlagsReleaseTest : SysuiTestCase() { mResources, mSystemProperties, deviceConfig, - serverFlagReader, - mDumpManager) - } - - @After - fun onFinished() { - // The dump manager should be registered with even for the release version, but that's it. - verify(mDumpManager).registerDumpable(any(), any()) - verifyNoMoreInteractions(mDumpManager) + serverFlagReader) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt new file mode 100644 index 0000000000000..4c61138707372 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagCommandTest.kt @@ -0,0 +1,84 @@ +/* + * 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.test.suitebuilder.annotation.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.any +import java.io.PrintWriter +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SmallTest +class FlagCommandTest : SysuiTestCase() { + + @Mock private lateinit var featureFlags: FeatureFlagsDebug + @Mock private lateinit var pw: PrintWriter + private val flagMap = mutableMapOf>() + private val flagA = UnreleasedFlag(500) + private val flagB = ReleasedFlag(501) + + private lateinit var cmd: FlagCommand + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + whenever(featureFlags.isEnabled(any(UnreleasedFlag::class.java))).thenReturn(false) + whenever(featureFlags.isEnabled(any(ReleasedFlag::class.java))).thenReturn(true) + flagMap.put(flagA.id, flagA) + flagMap.put(flagB.id, flagB) + + cmd = FlagCommand(featureFlags, flagMap) + } + + @Test + fun noOpCommand() { + cmd.execute(pw, ArrayList()) + Mockito.verify(pw, Mockito.atLeastOnce()).println() + Mockito.verify(featureFlags).isEnabled(flagA) + Mockito.verify(featureFlags).isEnabled(flagB) + } + + @Test + fun readFlagCommand() { + cmd.execute(pw, listOf(flagA.id.toString())) + Mockito.verify(featureFlags).isEnabled(flagA) + } + + @Test + fun setFlagCommand() { + cmd.execute(pw, listOf(flagB.id.toString(), "on")) + Mockito.verify(featureFlags).setBooleanFlagInternal(flagB, true) + } + + @Test + fun toggleFlagCommand() { + cmd.execute(pw, listOf(flagB.id.toString(), "toggle")) + Mockito.verify(featureFlags).setBooleanFlagInternal(flagB, false) + } + + @Test + fun eraseFlagCommand() { + cmd.execute(pw, listOf(flagA.id.toString(), "erase")) + Mockito.verify(featureFlags).eraseFlag(flagA) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt index c56fdb17b5f13..5d52be2675e3f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt @@ -16,6 +16,8 @@ package com.android.systemui.flags +import java.io.PrintWriter + class FakeFeatureFlags : FeatureFlags { private val booleanFlags = mutableMapOf() private val stringFlags = mutableMapOf() @@ -106,6 +108,10 @@ class FakeFeatureFlags : FeatureFlags { } } + override fun dump(writer: PrintWriter, args: Array?) { + // no-op + } + private fun flagName(flagId: Int): String { return knownFlagNames[flagId] ?: "UNKNOWN(id=$flagId)" }