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 flagsListHasDuplicateSymbolsnull64 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 if [old], [new], and [newAfterLocalModification] are all different: 78 * Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (changed: hB.iGR) | New after local 79 * modification: EnaihBcRso.qInGR (changed: .n) 80 * 81 * Example if [old] and [new] are the same: 82 * EnaihBcRso.qiNGR (unchanged) 83 * 84 * A capital character signifies the flag is set and a lowercase character signifies that the 85 * flag isn't set. The flag states will be logged in the same order as the passed-in lists. 86 * 87 * The difference between states is written between parentheses, and won't be included if there 88 * is no difference. the new-after-modification state also won't be included if there's no 89 * difference from the new state. 90 * 91 * @param old the disable state that had been previously sent. Null if we don't need to log the 92 * previously sent state. 93 * @param new the new disable state that has just been sent. 94 * @param newAfterLocalModification the new disable states after a class has locally modified 95 * them. Null if the class does not locally modify. 96 */ getDisableFlagsStringnull97 fun getDisableFlagsString( 98 old: DisableState? = null, 99 new: DisableState, 100 newAfterLocalModification: DisableState? = null 101 ): String { 102 val builder = StringBuilder("Received new disable state: ") 103 104 // This if/else has slightly repetitive code but is easier to read. 105 if (old != null && old != new) { 106 builder.append("Old: ") 107 builder.append(getFlagsString(old)) 108 builder.append(" | ") 109 builder.append("New: ") 110 builder.append(getFlagsString(new)) 111 builder.append(" ") 112 builder.append(getDiffString(old, new)) 113 } else if (old != null && old == new) { 114 // If old and new are the same, we only need to print one of them. 115 builder.append(getFlagsString(new)) 116 builder.append(" ") 117 builder.append(getDiffString(old, new)) 118 } else { // old == null 119 builder.append(getFlagsString(new)) 120 // Don't get a diff string because we have no [old] to compare with. 121 } 122 123 if (newAfterLocalModification != null && new != newAfterLocalModification) { 124 builder.append(" | New after local modification: ") 125 builder.append(getFlagsString(newAfterLocalModification)) 126 builder.append(" ") 127 builder.append(getDiffString(new, newAfterLocalModification)) 128 } 129 130 return builder.toString() 131 } 132 133 /** 134 * Returns a string representing the difference between [old] and [new]. 135 * 136 * - If [old] was "abc.DE" and [new] was "aBC.De", the difference returned would be 137 * "(changed: BC.e)". 138 * - If [old] and [new] are the same, the difference returned would be "(unchanged)". 139 */ getDiffStringnull140 private fun getDiffString(old: DisableState, new: DisableState): String { 141 if (old == new) { 142 return "(unchanged)" 143 } 144 145 val builder = StringBuilder("(") 146 builder.append("changed: ") 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". */ getFlagsStringnull165 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 */ getFlagStatusnull186 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)