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.systemui.flags 18 19 import android.os.Build 20 import android.util.Log 21 22 /** 23 * Utilities for writing your own objects to uphold refactor flag conventions. 24 * 25 * Example usage: 26 * ``` 27 * object SomeRefactor { 28 * const val FLAG_NAME = Flags.SOME_REFACTOR 29 * val token: FlagToken get() = FlagToken(FLAG_NAME, isEnabled) 30 * @JvmStatic inline val isEnabled get() = Flags.someRefactor() 31 * @JvmStatic inline fun isUnexpectedlyInLegacyMode() = 32 * RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) 33 * @JvmStatic inline fun assertInLegacyMode() = 34 * RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) 35 * } 36 * ``` 37 * 38 * Legacy mode crashes can be disabled with the command: 39 * ``` 40 * adb shell setprop log.tag.RefactorFlagAssert silent 41 * ``` 42 */ 43 @Suppress("NOTHING_TO_INLINE") 44 object RefactorFlagUtils { 45 /** 46 * Called to ensure code is only run when the flag is enabled. This protects users from the 47 * unintended behaviors caused by accidentally running new logic, while also crashing on an eng 48 * build to ensure that the refactor author catches issues in testing. 49 * 50 * Example usage: 51 * ``` 52 * public void setNewController(SomeController someController) { 53 * if (SomeRefactor.isUnexpectedlyInLegacyMode()) return; 54 * mSomeController = someController; 55 * } 56 * ``` 57 */ isUnexpectedlyInLegacyModenull58 inline fun isUnexpectedlyInLegacyMode(isEnabled: Boolean, flagName: Any): Boolean { 59 val inLegacyMode = !isEnabled 60 if (inLegacyMode) { 61 assertOnEngBuild("New code path expects $flagName to be enabled.") 62 } 63 return inLegacyMode 64 } 65 66 /** 67 * Called to ensure code is only run when the flag is disabled. This will throw an exception if 68 * the flag is enabled to ensure that the refactor author catches issues in testing. 69 * 70 * Example usage: 71 * ``` 72 * public void setSomeLegacyController(SomeController someController) { 73 * SomeRefactor.assertInLegacyMode(); 74 * mSomeController = someController; 75 * } 76 * ```` 77 */ assertInLegacyModenull78 inline fun assertInLegacyMode(isEnabled: Boolean, flagName: Any) = 79 check(!isEnabled) { "Legacy code path not supported when $flagName is enabled." } 80 81 /** 82 * Called to ensure the new code is only run when the flag is enabled. This will throw an 83 * exception if the flag is disabled to ensure that the refactor author catches issues in 84 * testing. 85 * 86 * Example usage: 87 * ``` 88 * public void setSomeNewController(SomeController someController) { 89 * SomeRefactor.assertInNewMode(); 90 * mSomeController = someController; 91 * } 92 * ```` 93 */ assertInNewModenull94 inline fun assertInNewMode(isEnabled: Boolean, flagName: Any) = 95 check(isEnabled) { "New code path not supported when $flagName is disabled." } 96 97 /** 98 * This will [Log.wtf] with the given message, assuming [ASSERT_TAG] is loggable at that level. 99 * This means an engineer can prevent this from crashing by running the command: 100 * ``` 101 * adb shell setprop log.tag.RefactorFlagAssert silent 102 * ``` 103 */ assertOnEngBuildnull104 fun assertOnEngBuild(message: String) { 105 if (Log.isLoggable(ASSERT_TAG, Log.ASSERT)) { 106 val exception = if (Build.isDebuggable()) IllegalStateException(message) else null 107 Log.wtf(ASSERT_TAG, message, exception) 108 } else if (Log.isLoggable(STANDARD_TAG, Log.WARN)) { 109 Log.w(STANDARD_TAG, message) 110 } 111 } 112 113 /** 114 * Tag used to determine if an incorrect flag guard should crash System UI running an eng build. 115 * This is enabled by default. To disable, run: 116 * ``` 117 * adb shell setprop log.tag.RefactorFlagAssert silent 118 * ``` 119 */ 120 private const val ASSERT_TAG = "RefactorFlagAssert" 121 122 /** Tag used for non-crashing logs or when the [ASSERT_TAG] has been silenced. */ 123 private const val STANDARD_TAG = "RefactorFlag" 124 } 125 126 /** An object which allows dependency tracking */ 127 data class FlagToken(val name: String, val isEnabled: Boolean) { toStringnull128 override fun toString(): String = "$name (${if (isEnabled) "enabled" else "disabled"})" 129 } 130