1 /** 2 * Copyright (C) 2023 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.internal.config.sysui; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.os.Build; 22 import android.os.SystemProperties; 23 import android.util.Log; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 27 /** 28 * Provides a central definition of debug SystemUI's SystemProperties flags, and their defaults. 29 * 30 * The main feature of this class is that it encodes a system-wide default for each flag which can 31 * be updated by engineers with a single-line CL. 32 * 33 * NOTE: Because flag values returned by this class are not cached, it is important that developers 34 * understand the intricacies of changing values and how that applies to their own code. 35 * Generally, the best practice is to set the property, and then restart the device so that any 36 * processes with stale state can be updated. However, if your code has no state derived from the 37 * flag value and queries it any time behavior is relevant, then it may be safe to change the flag 38 * and not immediately reboot. 39 * 40 * To enable flags in debuggable builds, use the following commands: 41 * 42 * $ adb shell setprop persist.sysui.whatever_the_flag true 43 * $ adb reboot 44 * 45 * @hide 46 */ 47 public class SystemUiSystemPropertiesFlags { 48 49 /** The teamfood flag allows multiple features to be opted into at once. */ 50 public static final Flag TEAMFOOD = devFlag("persist.sysui.teamfood"); 51 52 /** 53 * Flags related to notification features 54 */ 55 public static final class NotificationFlags { 56 57 /** Gating the logging of DND state change events. */ 58 public static final Flag LOG_DND_STATE_EVENTS = 59 releasedFlag("persist.sysui.notification.log_dnd_state_events"); 60 61 /** Gating storing NotificationRankingUpdate ranking map in shared memory. */ 62 public static final Flag RANKING_UPDATE_ASHMEM = devFlag( 63 "persist.sysui.notification.ranking_update_ashmem"); 64 65 public static final Flag PROPAGATE_CHANNEL_UPDATES_TO_CONVERSATIONS = releasedFlag( 66 "persist.sysui.notification.propagate_channel_updates_to_conversations"); 67 68 // TODO b/291899544: for released flags, use resource config values 69 /** Value used by polite notif. feature */ 70 public static final Flag NOTIF_COOLDOWN_T1 = devFlag( 71 "persist.debug.sysui.notification.notif_cooldown_t1", 60000); 72 /** Value used by polite notif. feature */ 73 public static final Flag NOTIF_COOLDOWN_T2 = devFlag( 74 "persist.debug.sysui.notification.notif_cooldown_t2", 10000); 75 /** Value used by polite notif. feature */ 76 public static final Flag NOTIF_VOLUME1 = devFlag( 77 "persist.debug.sysui.notification.notif_volume1", 30); 78 public static final Flag NOTIF_VOLUME2 = devFlag( 79 "persist.debug.sysui.notification.notif_volume2", 0); 80 /** Value used by polite notif. feature. -1 to ignore the counter */ 81 public static final Flag NOTIF_COOLDOWN_COUNTER_RESET = devFlag( 82 "persist.debug.sysui.notification.notif_cooldown_counter_reset", 10); 83 84 /** Value used by polite notif. feature */ 85 public static final Flag NOTIF_AVALANCHE_TIMEOUT = devFlag( 86 "persist.debug.sysui.notification.notif_avalanche_timeout", 120_000); 87 88 /** b/303716154: For debugging only: use short bitmap duration. */ 89 public static final Flag DEBUG_SHORT_BITMAP_DURATION = devFlag( 90 "persist.sysui.notification.debug_short_bitmap_duration"); 91 } 92 93 //// == End of flags. Everything below this line is the implementation. == //// 94 95 /** The interface used for resolving SystemUI SystemProperties Flags to booleans. */ 96 public interface FlagResolver { 97 /** Is the flag enabled? */ isEnabled(Flag flag)98 boolean isEnabled(Flag flag); 99 /** Get the flag value (integer) */ getIntValue(Flag flag)100 int getIntValue(Flag flag); 101 /** Get the flag value (string) */ getStringValue(Flag flag)102 String getStringValue(Flag flag); 103 } 104 105 /** The primary, immutable resolver returned by getResolver() */ 106 private static final FlagResolver 107 MAIN_RESOLVER = 108 Build.IS_DEBUGGABLE ? new DebugResolver() : new ProdResolver(); 109 110 /** 111 * On debuggable builds, this can be set to override the resolver returned by getResolver(). 112 * This can be useful to override flags when testing components that do not allow injecting the 113 * SystemUiPropertiesFlags resolver they use. 114 * Always set this to null when tests tear down. 115 */ 116 @VisibleForTesting 117 public static FlagResolver TEST_RESOLVER = null; 118 119 /** Get the resolver for this device configuration. */ getResolver()120 public static FlagResolver getResolver() { 121 if (Build.IS_DEBUGGABLE && TEST_RESOLVER != null) { 122 Log.i("SystemUiSystemPropertiesFlags", "Returning debug resolver " + TEST_RESOLVER); 123 return TEST_RESOLVER; 124 } 125 return MAIN_RESOLVER; 126 } 127 128 /** 129 * Creates a flag that is disabled by default in debuggable builds. 130 * It can be enabled by setting this flag's SystemProperty to 1. 131 * 132 * This flag is ALWAYS disabled in release builds. 133 */ 134 @VisibleForTesting devFlag(String name)135 public static Flag devFlag(String name) { 136 return new Flag(name, false, null); 137 } 138 139 /** 140 * Creates a flag that with a default integer value in debuggable builds. 141 */ 142 @VisibleForTesting devFlag(String name, int defaultValue)143 public static Flag devFlag(String name, int defaultValue) { 144 return new Flag(name, defaultValue, null); 145 } 146 147 /** 148 * Creates a flag that with a default string value in debuggable builds. 149 */ 150 @VisibleForTesting devFlag(String name, String defaultValue)151 public static Flag devFlag(String name, String defaultValue) { 152 return new Flag(name, defaultValue, null); 153 } 154 155 /** 156 * Creates a flag that is disabled by default in debuggable builds. 157 * It can be enabled or force-disabled by setting this flag's SystemProperty to 1 or 0. 158 * If this flag's SystemProperty is not set, the flag can be enabled by setting the 159 * TEAMFOOD flag's SystemProperty to 1. 160 * 161 * This flag is ALWAYS disabled in release builds. 162 */ 163 @VisibleForTesting teamfoodFlag(String name)164 public static Flag teamfoodFlag(String name) { 165 return new Flag(name, false, TEAMFOOD); 166 } 167 168 /** 169 * Creates a flag that is enabled by default in debuggable builds. 170 * It can be enabled by setting this flag's SystemProperty to 0. 171 * 172 * This flag is ALWAYS enabled in release builds. 173 */ 174 @VisibleForTesting releasedFlag(String name)175 public static Flag releasedFlag(String name) { 176 return new Flag(name, true, null); 177 } 178 179 /** Represents a developer-switchable gate for a feature. */ 180 public static final class Flag { 181 public final String mSysPropKey; 182 public final boolean mDefaultValue; 183 public final int mDefaultIntValue; 184 public final String mDefaultStringValue; 185 @Nullable 186 public final Flag mDebugDefault; 187 188 /** constructs a new flag. only visible for testing the class */ 189 @VisibleForTesting Flag(@onNull String sysPropKey, boolean defaultValue, @Nullable Flag debugDefault)190 public Flag(@NonNull String sysPropKey, boolean defaultValue, @Nullable Flag debugDefault) { 191 mSysPropKey = sysPropKey; 192 mDefaultValue = defaultValue; 193 mDebugDefault = debugDefault; 194 mDefaultIntValue = 0; 195 mDefaultStringValue = null; 196 } 197 Flag(@onNull String sysPropKey, int defaultValue, @Nullable Flag debugDefault)198 public Flag(@NonNull String sysPropKey, int defaultValue, @Nullable Flag debugDefault) { 199 mSysPropKey = sysPropKey; 200 mDefaultIntValue = defaultValue; 201 mDebugDefault = debugDefault; 202 mDefaultValue = false; 203 mDefaultStringValue = null; 204 } 205 Flag(@onNull String sysPropKey, String defaultValue, @Nullable Flag debugDefault)206 public Flag(@NonNull String sysPropKey, String defaultValue, @Nullable Flag debugDefault) { 207 mSysPropKey = sysPropKey; 208 mDefaultStringValue = defaultValue; 209 mDebugDefault = debugDefault; 210 mDefaultValue = false; 211 mDefaultIntValue = 0; 212 } 213 } 214 215 /** Implementation of the interface used in release builds. */ 216 @VisibleForTesting 217 public static final class ProdResolver implements 218 FlagResolver { 219 @Override isEnabled(Flag flag)220 public boolean isEnabled(Flag flag) { 221 return flag.mDefaultValue; 222 } 223 224 @Override getIntValue(Flag flag)225 public int getIntValue(Flag flag) { 226 return flag.mDefaultIntValue; 227 } 228 229 @Override getStringValue(Flag flag)230 public String getStringValue(Flag flag) { 231 return flag.mDefaultStringValue; 232 } 233 } 234 235 /** Implementation of the interface used in debuggable builds. */ 236 @VisibleForTesting 237 public static class DebugResolver implements FlagResolver { 238 @Override isEnabled(Flag flag)239 public final boolean isEnabled(Flag flag) { 240 if (flag.mDebugDefault == null) { 241 return getBoolean(flag.mSysPropKey, flag.mDefaultValue); 242 } 243 return getBoolean(flag.mSysPropKey, isEnabled(flag.mDebugDefault)); 244 } 245 246 /** Look up the value; overridable for tests to avoid needing to set SystemProperties */ 247 @VisibleForTesting getBoolean(String key, boolean defaultValue)248 public boolean getBoolean(String key, boolean defaultValue) { 249 return SystemProperties.getBoolean(key, defaultValue); 250 } 251 252 /** Look up the value; overridable for tests to avoid needing to set SystemProperties */ 253 @VisibleForTesting getIntValue(Flag flag)254 public int getIntValue(Flag flag) { 255 if (flag.mDebugDefault == null) { 256 return SystemProperties.getInt(flag.mSysPropKey, flag.mDefaultIntValue); 257 } 258 return SystemProperties.getInt(flag.mSysPropKey, getIntValue(flag.mDebugDefault)); 259 } 260 261 /** Look up the value; overridable for tests to avoid needing to set SystemProperties */ 262 @VisibleForTesting getStringValue(Flag flag)263 public String getStringValue(Flag flag) { 264 if (flag.mDebugDefault == null) { 265 return SystemProperties.get(flag.mSysPropKey, flag.mDefaultStringValue); 266 } 267 return SystemProperties.get(flag.mSysPropKey, getStringValue(flag.mDebugDefault)); 268 } 269 } 270 } 271