1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.flags; 18 19 import static com.android.systemui.flags.FlagsCommonModule.ALL_FLAGS; 20 21 import androidx.annotation.NonNull; 22 23 import com.android.systemui.statusbar.commandline.Command; 24 25 import java.io.PrintWriter; 26 import java.util.List; 27 import java.util.Map; 28 29 import javax.inject.Inject; 30 import javax.inject.Named; 31 32 /** 33 * A {@link Command} used to flip flags in SystemUI. 34 */ 35 public class FlagCommand implements Command { 36 public static final String FLAG_COMMAND = "flag"; 37 38 private final List<String> mOnCommands = List.of("true", "on", "1", "enabled"); 39 private final List<String> mOffCommands = List.of("false", "off", "0", "disable"); 40 private final List<String> mSetCommands = List.of("set", "put"); 41 private final FeatureFlagsDebug mFeatureFlags; 42 private final Map<String, Flag<?>> mAllFlags; 43 44 @Inject FlagCommand( FeatureFlagsDebug featureFlags, @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags )45 FlagCommand( 46 FeatureFlagsDebug featureFlags, 47 @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags 48 ) { 49 mFeatureFlags = featureFlags; 50 mAllFlags = allFlags; 51 } 52 53 @Override execute(@onNull PrintWriter pw, @NonNull List<String> args)54 public void execute(@NonNull PrintWriter pw, @NonNull List<String> args) { 55 if (args.size() == 0) { 56 pw.println("Error: no flag name supplied"); 57 help(pw); 58 pw.println(); 59 printKnownFlags(pw); 60 return; 61 } 62 63 String name = args.get(0); 64 if (!mAllFlags.containsKey(name)) { 65 pw.println("Unknown flag name: " + name); 66 pw.println(); 67 printKnownFlags(pw); 68 return; 69 } 70 71 Flag<?> flag = mAllFlags.get(name); 72 73 String cmd = ""; 74 if (args.size() > 1) { 75 cmd = args.get(1).toLowerCase(); 76 } 77 78 if ("erase".equals(cmd) || "reset".equals(cmd)) { 79 if (args.size() > 2) { 80 pw.println("Invalid number of arguments to reset a flag."); 81 help(pw); 82 return; 83 } 84 85 mFeatureFlags.eraseFlag(flag); 86 return; 87 } 88 89 boolean shouldSet = true; 90 if (args.size() == 1) { 91 shouldSet = false; 92 } 93 if (isBooleanFlag(flag)) { 94 if (args.size() > 2) { 95 pw.println("Invalid number of arguments for a boolean flag."); 96 help(pw); 97 return; 98 } 99 boolean newValue = isBooleanFlagEnabled(flag); 100 if ("toggle".equals(cmd)) { 101 newValue = !newValue; 102 } else if (mOnCommands.contains(cmd)) { 103 newValue = true; 104 } else if (mOffCommands.contains(cmd)) { 105 newValue = false; 106 } else if (shouldSet) { 107 pw.println("Invalid on/off argument supplied"); 108 help(pw); 109 return; 110 } 111 112 pw.println("Flag " + name + " is " + newValue); 113 pw.flush(); // Next command will restart sysui, so flush before we do so. 114 if (shouldSet) { 115 mFeatureFlags.setBooleanFlagInternal(flag, newValue); 116 } 117 return; 118 119 } else if (isStringFlag(flag)) { 120 if (shouldSet) { 121 if (args.size() != 3) { 122 pw.println("Invalid number of arguments a StringFlag."); 123 help(pw); 124 return; 125 } else if (!mSetCommands.contains(cmd)) { 126 pw.println("Unknown command: " + cmd); 127 help(pw); 128 return; 129 } 130 String value = args.get(2); 131 pw.println("Setting Flag " + name + " to " + value); 132 pw.flush(); // Next command will restart sysui, so flush before we do so. 133 mFeatureFlags.setStringFlagInternal(flag, args.get(2)); 134 } else { 135 pw.println("Flag " + name + " is " + getStringFlag(flag)); 136 } 137 return; 138 } else if (isIntFlag(flag)) { 139 if (shouldSet) { 140 if (args.size() != 3) { 141 pw.println("Invalid number of arguments for an IntFlag."); 142 help(pw); 143 return; 144 } else if (!mSetCommands.contains(cmd)) { 145 pw.println("Unknown command: " + cmd); 146 help(pw); 147 return; 148 } 149 int value = Integer.parseInt(args.get(2)); 150 pw.println("Setting Flag " + name + " to " + value); 151 pw.flush(); // Next command will restart sysui, so flush before we do so. 152 mFeatureFlags.setIntFlagInternal(flag, value); 153 } else { 154 pw.println("Flag " + name + " is " + getIntFlag(flag)); 155 } 156 return; 157 } 158 } 159 160 @Override help(PrintWriter pw)161 public void help(PrintWriter pw) { 162 pw.println("Usage: adb shell cmd statusbar flag <id> [options]"); 163 pw.println(); 164 pw.println(" Boolean Flag Options: " 165 + "[true|false|1|0|on|off|enable|disable|toggle|erase|reset]"); 166 pw.println(" String Flag Options: [set|put \"<value>\"]"); 167 pw.println(" Int Flag Options: [set|put <value>]"); 168 pw.println(); 169 pw.println("The id can either be a numeric integer or the corresponding field name"); 170 pw.println( 171 "If no argument is supplied after the id, the flags runtime value is output"); 172 } 173 isBooleanFlag(Flag<?> flag)174 private boolean isBooleanFlag(Flag<?> flag) { 175 return (flag instanceof BooleanFlag) 176 || (flag instanceof ResourceBooleanFlag) 177 || (flag instanceof SysPropFlag); 178 } 179 isBooleanFlagEnabled(Flag<?> flag)180 private boolean isBooleanFlagEnabled(Flag<?> flag) { 181 if (flag instanceof ReleasedFlag) { 182 return mFeatureFlags.isEnabled((ReleasedFlag) flag); 183 } else if (flag instanceof UnreleasedFlag) { 184 return mFeatureFlags.isEnabled((UnreleasedFlag) flag); 185 } else if (flag instanceof ResourceBooleanFlag) { 186 return mFeatureFlags.isEnabled((ResourceBooleanFlag) flag); 187 } else if (flag instanceof SysPropFlag) { 188 return mFeatureFlags.isEnabled((SysPropBooleanFlag) flag); 189 } 190 191 return false; 192 } 193 isStringFlag(Flag<?> flag)194 private boolean isStringFlag(Flag<?> flag) { 195 return (flag instanceof StringFlag) || (flag instanceof ResourceStringFlag); 196 } 197 getStringFlag(Flag<?> flag)198 private String getStringFlag(Flag<?> flag) { 199 if (flag instanceof StringFlag) { 200 return mFeatureFlags.getString((StringFlag) flag); 201 } else if (flag instanceof ResourceStringFlag) { 202 return mFeatureFlags.getString((ResourceStringFlag) flag); 203 } 204 205 return ""; 206 } 207 isIntFlag(Flag<?> flag)208 private boolean isIntFlag(Flag<?> flag) { 209 return (flag instanceof IntFlag) || (flag instanceof ResourceIntFlag); 210 } 211 getIntFlag(Flag<?> flag)212 private int getIntFlag(Flag<?> flag) { 213 if (flag instanceof IntFlag) { 214 return mFeatureFlags.getInt((IntFlag) flag); 215 } else if (flag instanceof ResourceIntFlag) { 216 return mFeatureFlags.getInt((ResourceIntFlag) flag); 217 } 218 219 return 0; 220 } 221 flagNameToId(String flagName)222 private int flagNameToId(String flagName) { 223 Map<String, Flag<?>> flagFields = FlagsFactory.INSTANCE.getKnownFlags(); 224 for (String fieldName : flagFields.keySet()) { 225 if (flagName.equals(fieldName)) { 226 return flagFields.get(fieldName).getId(); 227 } 228 } 229 230 return 0; 231 } 232 printKnownFlags(PrintWriter pw)233 private void printKnownFlags(PrintWriter pw) { 234 Map<String, Flag<?>> fields = FlagsFactory.INSTANCE.getKnownFlags(); 235 236 int longestFieldName = 0; 237 for (String fieldName : fields.keySet()) { 238 longestFieldName = Math.max(longestFieldName, fieldName.length()); 239 } 240 241 pw.println("Known Flags:"); 242 pw.print("Flag Name"); 243 for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) { 244 pw.print(" "); 245 } 246 pw.println(" Value"); 247 for (int i = 0; i < longestFieldName; i++) { 248 pw.print("="); 249 } 250 pw.println(" ========"); 251 for (String fieldName : fields.keySet()) { 252 Flag<?> flag = fields.get(fieldName); 253 if (!mAllFlags.containsKey(flag.getName())) { 254 continue; 255 } 256 pw.print(fieldName); 257 int fieldWidth = fieldName.length(); 258 for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) { 259 pw.print(" "); 260 } 261 pw.print(" "); 262 if (isBooleanFlag(flag)) { 263 pw.println(isBooleanFlagEnabled(flag)); 264 } else if (isStringFlag(flag)) { 265 pw.println(getStringFlag(flag)); 266 } else if (isIntFlag(flag)) { 267 pw.println(getIntFlag(flag)); 268 } else { 269 pw.println("<unknown flag type>"); 270 } 271 } 272 } 273 } 274