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 /** 58 * FOR DEVELOPMENT / TESTING ONLY!!! 59 * Forcibly demote *ALL* FSI notifications as if no apps have the app op permission. 60 * NOTE: enabling this implies SHOW_STICKY_HUN_FOR_DENIED_FSI in SystemUI 61 */ 62 public static final Flag FSI_FORCE_DEMOTE = 63 devFlag("persist.sysui.notification.fsi_force_demote"); 64 65 /** Gating the feature which shows FSI-denied notifications as Sticky HUNs */ 66 public static final Flag SHOW_STICKY_HUN_FOR_DENIED_FSI = 67 releasedFlag("persist.sysui.notification.show_sticky_hun_for_denied_fsi"); 68 69 /** Gating the redaction of OTP notifications on the lockscreen */ 70 public static final Flag OTP_REDACTION = 71 devFlag("persist.sysui.notification.otp_redaction"); 72 73 /** Gating the logging of DND state change events. */ 74 public static final Flag LOG_DND_STATE_EVENTS = 75 releasedFlag("persist.sysui.notification.log_dnd_state_events"); 76 77 /** Gating the holding of WakeLocks until NLSes are told about a new notification. */ 78 public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION = 79 releasedFlag("persist.sysui.notification.wake_lock_for_posting_notification"); 80 81 /** Gating storing NotificationRankingUpdate ranking map in shared memory. */ 82 public static final Flag RANKING_UPDATE_ASHMEM = devFlag( 83 "persist.sysui.notification.ranking_update_ashmem"); 84 85 public static final Flag PROPAGATE_CHANNEL_UPDATES_TO_CONVERSATIONS = devFlag( 86 "persist.sysui.notification.propagate_channel_updates_to_conversations"); 87 } 88 89 //// == End of flags. Everything below this line is the implementation. == //// 90 91 /** The interface used for resolving SystemUI SystemProperties Flags to booleans. */ 92 public interface FlagResolver { 93 /** Is the flag enabled? */ isEnabled(Flag flag)94 boolean isEnabled(Flag flag); 95 } 96 97 /** The primary, immutable resolver returned by getResolver() */ 98 private static final FlagResolver 99 MAIN_RESOLVER = 100 Build.IS_DEBUGGABLE ? new DebugResolver() : new ProdResolver(); 101 102 /** 103 * On debuggable builds, this can be set to override the resolver returned by getResolver(). 104 * This can be useful to override flags when testing components that do not allow injecting the 105 * SystemUiPropertiesFlags resolver they use. 106 * Always set this to null when tests tear down. 107 */ 108 @VisibleForTesting 109 public static FlagResolver TEST_RESOLVER = null; 110 111 /** Get the resolver for this device configuration. */ getResolver()112 public static FlagResolver getResolver() { 113 if (Build.IS_DEBUGGABLE && TEST_RESOLVER != null) { 114 Log.i("SystemUiSystemPropertiesFlags", "Returning debug resolver " + TEST_RESOLVER); 115 return TEST_RESOLVER; 116 } 117 return MAIN_RESOLVER; 118 } 119 120 /** 121 * Creates a flag that is disabled by default in debuggable builds. 122 * It can be enabled by setting this flag's SystemProperty to 1. 123 * 124 * This flag is ALWAYS disabled in release builds. 125 */ 126 @VisibleForTesting devFlag(String name)127 public static Flag devFlag(String name) { 128 return new Flag(name, false, null); 129 } 130 131 /** 132 * Creates a flag that is disabled by default in debuggable builds. 133 * It can be enabled or force-disabled by setting this flag's SystemProperty to 1 or 0. 134 * If this flag's SystemProperty is not set, the flag can be enabled by setting the 135 * TEAMFOOD flag's SystemProperty to 1. 136 * 137 * This flag is ALWAYS disabled in release builds. 138 */ 139 @VisibleForTesting teamfoodFlag(String name)140 public static Flag teamfoodFlag(String name) { 141 return new Flag(name, false, TEAMFOOD); 142 } 143 144 /** 145 * Creates a flag that is enabled by default in debuggable builds. 146 * It can be enabled by setting this flag's SystemProperty to 0. 147 * 148 * This flag is ALWAYS enabled in release builds. 149 */ 150 @VisibleForTesting releasedFlag(String name)151 public static Flag releasedFlag(String name) { 152 return new Flag(name, true, null); 153 } 154 155 /** Represents a developer-switchable gate for a feature. */ 156 public static final class Flag { 157 public final String mSysPropKey; 158 public final boolean mDefaultValue; 159 @Nullable 160 public final Flag mDebugDefault; 161 162 /** constructs a new flag. only visible for testing the class */ 163 @VisibleForTesting Flag(@onNull String sysPropKey, boolean defaultValue, @Nullable Flag debugDefault)164 public Flag(@NonNull String sysPropKey, boolean defaultValue, @Nullable Flag debugDefault) { 165 mSysPropKey = sysPropKey; 166 mDefaultValue = defaultValue; 167 mDebugDefault = debugDefault; 168 } 169 } 170 171 /** Implementation of the interface used in release builds. */ 172 @VisibleForTesting 173 public static final class ProdResolver implements 174 FlagResolver { 175 @Override isEnabled(Flag flag)176 public boolean isEnabled(Flag flag) { 177 return flag.mDefaultValue; 178 } 179 } 180 181 /** Implementation of the interface used in debuggable builds. */ 182 @VisibleForTesting 183 public static class DebugResolver implements FlagResolver { 184 @Override isEnabled(Flag flag)185 public final boolean isEnabled(Flag flag) { 186 if (flag.mDebugDefault == null) { 187 return getBoolean(flag.mSysPropKey, flag.mDefaultValue); 188 } 189 return getBoolean(flag.mSysPropKey, isEnabled(flag.mDebugDefault)); 190 } 191 192 /** Look up the value; overridable for tests to avoid needing to set SystemProperties */ 193 @VisibleForTesting getBoolean(String key, boolean defaultValue)194 public boolean getBoolean(String key, boolean defaultValue) { 195 return SystemProperties.getBoolean(key, defaultValue); 196 } 197 } 198 } 199