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 FeatureFlagsClassicDebug mFeatureFlags; 42 private final Map<String, Flag<?>> mAllFlags; 43 44 @Inject FlagCommand( FeatureFlagsClassicDebug featureFlags, @Named(ALL_FLAGS) Map<String, Flag<?>> allFlags )45 FlagCommand( 46 FeatureFlagsClassicDebug 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 isTeamfoodFlag(Flag<?> flag)180 private Boolean isTeamfoodFlag(Flag<?> flag) { 181 if (!(flag instanceof BooleanFlag)) { 182 return null; 183 } 184 return flag.getTeamfood(); 185 } 186 isBooleanFlagEnabled(Flag<?> flag)187 private boolean isBooleanFlagEnabled(Flag<?> flag) { 188 if (flag instanceof ReleasedFlag) { 189 return mFeatureFlags.isEnabled((ReleasedFlag) flag); 190 } else if (flag instanceof UnreleasedFlag) { 191 return mFeatureFlags.isEnabled((UnreleasedFlag) flag); 192 } else if (flag instanceof ResourceBooleanFlag) { 193 return mFeatureFlags.isEnabled((ResourceBooleanFlag) flag); 194 } else if (flag instanceof SysPropFlag) { 195 return mFeatureFlags.isEnabled((SysPropBooleanFlag) flag); 196 } 197 198 return false; 199 } 200 isStringFlag(Flag<?> flag)201 private boolean isStringFlag(Flag<?> flag) { 202 return (flag instanceof StringFlag) || (flag instanceof ResourceStringFlag); 203 } 204 getStringFlag(Flag<?> flag)205 private String getStringFlag(Flag<?> flag) { 206 if (flag instanceof StringFlag) { 207 return mFeatureFlags.getString((StringFlag) flag); 208 } else if (flag instanceof ResourceStringFlag) { 209 return mFeatureFlags.getString((ResourceStringFlag) flag); 210 } 211 212 return ""; 213 } 214 isIntFlag(Flag<?> flag)215 private boolean isIntFlag(Flag<?> flag) { 216 return (flag instanceof IntFlag) || (flag instanceof ResourceIntFlag); 217 } 218 getIntFlag(Flag<?> flag)219 private int getIntFlag(Flag<?> flag) { 220 if (flag instanceof IntFlag) { 221 return mFeatureFlags.getInt((IntFlag) flag); 222 } else if (flag instanceof ResourceIntFlag) { 223 return mFeatureFlags.getInt((ResourceIntFlag) flag); 224 } 225 226 return 0; 227 } 228 printKnownFlags(PrintWriter pw)229 private void printKnownFlags(PrintWriter pw) { 230 Map<String, Flag<?>> fields = FlagsFactory.INSTANCE.getKnownFlags(); 231 232 int longestFieldName = 0; 233 for (String fieldName : fields.keySet()) { 234 longestFieldName = Math.max(longestFieldName, fieldName.length()); 235 } 236 237 pw.println("Known Flags:"); 238 pw.print("Flag Name"); 239 for (int i = 0; i < longestFieldName - "Flag Name".length() + 1; i++) { 240 pw.print(" "); 241 } 242 pw.print(" Value "); 243 pw.println(" Teamfood?"); 244 for (int i = 0; i < longestFieldName; i++) { 245 pw.print("="); 246 } 247 pw.println(" ======= ==========="); 248 249 for (String fieldName : fields.keySet()) { 250 Flag<?> flag = fields.get(fieldName); 251 if (!mAllFlags.containsKey(flag.getName())) { 252 continue; 253 } 254 pw.print(fieldName); 255 int fieldWidth = fieldName.length(); 256 for (int i = 0; i < longestFieldName - fieldWidth + 1; i++) { 257 pw.print(" "); 258 } 259 pw.print(" "); 260 if (isBooleanFlag(flag)) { 261 boolean enabled = isBooleanFlagEnabled(flag); 262 pw.print(enabled); 263 if (enabled) { 264 pw.print(" "); 265 } else { 266 pw.print(" "); 267 } 268 Boolean teamfood = isTeamfoodFlag(flag); 269 if (teamfood != null) { 270 pw.print(teamfood); 271 } 272 pw.println(); 273 274 } else if (isStringFlag(flag)) { 275 pw.println(getStringFlag(flag)); 276 } else if (isIntFlag(flag)) { 277 pw.println(getIntFlag(flag)); 278 } else { 279 pw.println("<unknown flag type>"); 280 } 281 } 282 } 283 } 284