1 /* 2 * Copyright (C) 2021 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 package com.android.systemui.statusbar 17 18 import android.app.StatusBarManager.DISABLE_BACK 19 import android.app.StatusBarManager.DISABLE_CLOCK 20 import android.app.StatusBarManager.DISABLE_EXPAND 21 import android.app.StatusBarManager.DISABLE_HOME 22 import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS 23 import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS 24 import android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP 25 import android.app.StatusBarManager.DISABLE_RECENT 26 import android.app.StatusBarManager.DISABLE_SEARCH 27 import android.app.StatusBarManager.DISABLE_SYSTEM_INFO 28 import android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS 29 import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE 30 import android.app.StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS 31 import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS 32 import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS 33 import com.android.systemui.dagger.SysUISingleton 34 import javax.inject.Inject 35 36 /** 37 * A singleton that creates concise but readable strings representing the values of the disable 38 * flags for debugging. 39 * 40 * See [CommandQueue.disable] for information about disable flags. 41 * 42 * Note that, for both lists passed in, each flag must have a distinct [DisableFlag.flagIsSetSymbol] 43 * and distinct [DisableFlag.flagNotSetSymbol] within the list. If this isn't true, the logs could 44 * be ambiguous so an [IllegalArgumentException] is thrown. 45 */ 46 @SysUISingleton 47 class DisableFlagsLogger constructor( 48 private val disable1FlagsList: List<DisableFlag>, 49 private val disable2FlagsList: List<DisableFlag> 50 ) { 51 52 @Inject 53 constructor() : this(defaultDisable1FlagsList, defaultDisable2FlagsList) 54 55 init { 56 if (flagsListHasDuplicateSymbols(disable1FlagsList)) { 57 throw IllegalArgumentException("disable1 flags must have unique symbols") 58 } 59 if (flagsListHasDuplicateSymbols(disable2FlagsList)) { 60 throw IllegalArgumentException("disable2 flags must have unique symbols") 61 } 62 } 63 64 private fun flagsListHasDuplicateSymbols(list: List<DisableFlag>): Boolean { 65 val numDistinctFlagOffStatus = list.map { it.getFlagStatus(0) }.distinct().count() 66 val numDistinctFlagOnStatus = list 67 .map { it.getFlagStatus(Int.MAX_VALUE) } 68 .distinct() 69 .count() 70 return numDistinctFlagOffStatus < list.count() || numDistinctFlagOnStatus < list.count() 71 } 72 73 /** 74 * Returns a string representing the, old, new, and new-after-modification disable flag states, 75 * as well as the differences between each of the states. 76 * 77 * Example: 78 * Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (hB.iGR) | New after local modification: 79 * EnaihBcRso.qInGR (.n) 80 * 81 * A capital character signifies the flag is set and a lowercase character signifies that the 82 * flag isn't set. The flag states will be logged in the same order as the passed-in lists. 83 * 84 * The difference between states is written between parentheses, and won't be included if there 85 * is no difference. the new-after-modification state also won't be included if there's no 86 * difference from the new state. 87 * 88 * @param old the disable state that had been previously sent. Null if we don't need to log the 89 * previously sent state. 90 * @param new the new disable state that has just been sent. 91 * @param newAfterLocalModification the new disable states after a class has locally modified 92 * them. Null if the class does not locally modify. 93 */ 94 fun getDisableFlagsString( 95 old: DisableState? = null, 96 new: DisableState, 97 newAfterLocalModification: DisableState? = null 98 ): String { 99 val builder = StringBuilder("Received new disable state. ") 100 101 old?.let { 102 builder.append("Old: ") 103 builder.append(getFlagsString(old)) 104 builder.append(" | ") 105 } 106 107 builder.append("New: ") 108 if (old != null && old != new) { 109 builder.append(getFlagsStringWithDiff(old, new)) 110 } else { 111 builder.append(getFlagsString(new)) 112 } 113 114 if (newAfterLocalModification != null && new != newAfterLocalModification) { 115 builder.append(" | New after local modification: ") 116 builder.append(getFlagsStringWithDiff(new, newAfterLocalModification)) 117 } 118 119 return builder.toString() 120 } 121 122 /** 123 * Returns a string representing [new] state, as well as the difference from [old] to [new] 124 * (if there is one). 125 */ 126 private fun getFlagsStringWithDiff(old: DisableState, new: DisableState): String { 127 val builder = StringBuilder() 128 builder.append(getFlagsString(new)) 129 builder.append(" ") 130 builder.append(getDiffString(old, new)) 131 return builder.toString() 132 } 133 134 /** 135 * Returns a string representing the difference between [old] and [new], or an empty string if 136 * there is no difference. 137 * 138 * For example, if old was "abc.DE" and new was "aBC.De", the difference returned would be 139 * "(BC.e)". 140 */ 141 private fun getDiffString(old: DisableState, new: DisableState): String { 142 if (old == new) { 143 return "" 144 } 145 146 val builder = StringBuilder("(") 147 disable1FlagsList.forEach { 148 val newSymbol = it.getFlagStatus(new.disable1) 149 if (it.getFlagStatus(old.disable1) != newSymbol) { 150 builder.append(newSymbol) 151 } 152 } 153 builder.append(".") 154 disable2FlagsList.forEach { 155 val newSymbol = it.getFlagStatus(new.disable2) 156 if (it.getFlagStatus(old.disable2) != newSymbol) { 157 builder.append(newSymbol) 158 } 159 } 160 builder.append(")") 161 return builder.toString() 162 } 163 164 /** Returns a string representing the disable flag states, e.g. "EnaihBcRso.qiNGR". */ 165 private fun getFlagsString(state: DisableState): String { 166 val builder = StringBuilder("") 167 disable1FlagsList.forEach { builder.append(it.getFlagStatus(state.disable1)) } 168 builder.append(".") 169 disable2FlagsList.forEach { builder.append(it.getFlagStatus(state.disable2)) } 170 return builder.toString() 171 } 172 173 /** A POJO representing each disable flag. */ 174 class DisableFlag( 175 private val bitMask: Int, 176 private val flagIsSetSymbol: Char, 177 private val flagNotSetSymbol: Char 178 ) { 179 180 /** 181 * Returns a character representing whether or not this flag is set in [state]. 182 * 183 * A capital character signifies the flag is set and a lowercase character signifies that 184 * the flag isn't set. 185 */ 186 internal fun getFlagStatus(state: Int): Char = 187 if (0 != state and this.bitMask) this.flagIsSetSymbol 188 else this.flagNotSetSymbol 189 } 190 191 /** POJO to hold [disable1] and [disable2]. */ 192 data class DisableState(val disable1: Int, val disable2: Int) 193 } 194 195 // LINT.IfChange 196 private val defaultDisable1FlagsList: List<DisableFlagsLogger.DisableFlag> = listOf( 197 DisableFlagsLogger.DisableFlag(DISABLE_EXPAND, 'E', 'e'), 198 DisableFlagsLogger.DisableFlag(DISABLE_NOTIFICATION_ICONS, 'N', 'n'), 199 DisableFlagsLogger.DisableFlag(DISABLE_NOTIFICATION_ALERTS, 'A', 'a'), 200 DisableFlagsLogger.DisableFlag(DISABLE_SYSTEM_INFO, 'I', 'i'), 201 DisableFlagsLogger.DisableFlag(DISABLE_HOME, 'H', 'h'), 202 DisableFlagsLogger.DisableFlag(DISABLE_BACK, 'B', 'b'), 203 DisableFlagsLogger.DisableFlag(DISABLE_CLOCK, 'C', 'c'), 204 DisableFlagsLogger.DisableFlag(DISABLE_RECENT, 'R', 'r'), 205 DisableFlagsLogger.DisableFlag(DISABLE_SEARCH, 'S', 's'), 206 DisableFlagsLogger.DisableFlag(DISABLE_ONGOING_CALL_CHIP, 'O', 'o') 207 ) 208 209 private val defaultDisable2FlagsList: List<DisableFlagsLogger.DisableFlag> = listOf( 210 DisableFlagsLogger.DisableFlag(DISABLE2_QUICK_SETTINGS, 'Q', 'q'), 211 DisableFlagsLogger.DisableFlag(DISABLE2_SYSTEM_ICONS, 'I', 'i'), 212 DisableFlagsLogger.DisableFlag(DISABLE2_NOTIFICATION_SHADE, 'N', 'n'), 213 DisableFlagsLogger.DisableFlag(DISABLE2_GLOBAL_ACTIONS, 'G', 'g'), 214 DisableFlagsLogger.DisableFlag(DISABLE2_ROTATE_SUGGESTIONS, 'R', 'r') 215 ) 216 // LINT.ThenChange(frameworks/base/core/java/android/app/StatusBarManager.java)