diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index d9a339025afd1..ee50634492bba 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -52,7 +52,7 @@ import java.util.function.Consumer; */ @SystemService(Context.STATUS_BAR_SERVICE) public class StatusBarManager { - + // LINT.IfChange /** @hide */ public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND; /** @hide */ @@ -150,6 +150,7 @@ public class StatusBarManager { }) @Retention(RetentionPolicy.SOURCE) public @interface Disable2Flags {} + // LINT.ThenChange(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt) /** * Default disable flags for setup diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt new file mode 100644 index 0000000000000..cf34db233b065 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar + +import android.app.StatusBarManager.DISABLE_BACK +import android.app.StatusBarManager.DISABLE_CLOCK +import android.app.StatusBarManager.DISABLE_EXPAND +import android.app.StatusBarManager.DISABLE_HOME +import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS +import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS +import android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP +import android.app.StatusBarManager.DISABLE_RECENT +import android.app.StatusBarManager.DISABLE_SEARCH +import android.app.StatusBarManager.DISABLE_SYSTEM_INFO +import android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS +import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE +import android.app.StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS +import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS +import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject + +/** + * A singleton that creates concise but readable strings representing the values of the disable + * flags for debugging. + * + * See [CommandQueue.disable] for information about disable flags. + * + * Note that, for both lists passed in, each flag must have a distinct [DisableFlag.flagIsSetSymbol] + * and distinct [DisableFlag.flagNotSetSymbol] within the list. If this isn't true, the logs could + * be ambiguous so an [IllegalArgumentException] is thrown. + */ +@SysUISingleton +class DisableFlagsLogger constructor( + private val disable1FlagsList: List, + private val disable2FlagsList: List +) { + + @Inject + constructor() : this(defaultDisable1FlagsList, defaultDisable2FlagsList) + + init { + if (flagsListHasDuplicateSymbols(disable1FlagsList)) { + throw IllegalArgumentException("disable1 flags must have unique symbols") + } + if (flagsListHasDuplicateSymbols(disable2FlagsList)) { + throw IllegalArgumentException("disable2 flags must have unique symbols") + } + } + + private fun flagsListHasDuplicateSymbols(list: List): Boolean { + val numDistinctFlagOffStatus = list.map { it.getFlagStatus(0) }.distinct().count() + val numDistinctFlagOnStatus = list + .map { it.getFlagStatus(Int.MAX_VALUE) } + .distinct() + .count() + return numDistinctFlagOffStatus < list.count() || numDistinctFlagOnStatus < list.count() + } + + /** + * Returns a string representing the, old, new, and new-after-modification disable flag states, + * as well as the differences between each of the states. + * + * Example: + * Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (hB.iGR) | New after local modification: + * EnaihBcRso.qInGR (.n) + * + * A capital character signifies the flag is set and a lowercase character signifies that the + * flag isn't set. The flag states will be logged in the same order as the passed-in lists. + * + * The difference between states is written between parentheses, and won't be included if there + * is no difference. the new-after-modification state also won't be included if there's no + * difference from the new state. + * + * @param old the disable state that had been previously sent. + * @param new the new disable state that has just been sent. + * @param newAfterLocalModification the new disable states after a class has locally modified + * them. Null if the class does not locally modify. + */ + fun getDisableFlagsString( + old: DisableState, + new: DisableState, + newAfterLocalModification: DisableState? = null + ): String { + val builder = StringBuilder("Received new disable state. ") + builder.append("Old: ") + builder.append(getFlagsString(old)) + builder.append(" | New: ") + if (old != new) { + builder.append(getFlagsStringWithDiff(old, new)) + } else { + builder.append(getFlagsString(old)) + } + + if (newAfterLocalModification != null && new != newAfterLocalModification) { + builder.append(" | New after local modification: ") + builder.append(getFlagsStringWithDiff(new, newAfterLocalModification)) + } + + return builder.toString() + } + + /** + * Returns a string representing [new] state, as well as the difference from [old] to [new] + * (if there is one). + */ + private fun getFlagsStringWithDiff(old: DisableState, new: DisableState): String { + val builder = StringBuilder() + builder.append(getFlagsString(new)) + builder.append(" ") + builder.append(getDiffString(old, new)) + return builder.toString() + } + + /** + * Returns a string representing the difference between [old] and [new], or an empty string if + * there is no difference. + * + * For example, if old was "abc.DE" and new was "aBC.De", the difference returned would be + * "(BC.e)". + */ + private fun getDiffString(old: DisableState, new: DisableState): String { + if (old == new) { + return "" + } + + val builder = StringBuilder("(") + disable1FlagsList.forEach { + val newSymbol = it.getFlagStatus(new.disable1) + if (it.getFlagStatus(old.disable1) != newSymbol) { + builder.append(newSymbol) + } + } + builder.append(".") + disable2FlagsList.forEach { + val newSymbol = it.getFlagStatus(new.disable2) + if (it.getFlagStatus(old.disable2) != newSymbol) { + builder.append(newSymbol) + } + } + builder.append(")") + return builder.toString() + } + + /** Returns a string representing the disable flag states, e.g. "EnaihBcRso.qiNGR". */ + private fun getFlagsString(state: DisableState): String { + val builder = StringBuilder("") + disable1FlagsList.forEach { builder.append(it.getFlagStatus(state.disable1)) } + builder.append(".") + disable2FlagsList.forEach { builder.append(it.getFlagStatus(state.disable2)) } + return builder.toString() + } + + /** A POJO representing each disable flag. */ + class DisableFlag( + private val bitMask: Int, + private val flagIsSetSymbol: Char, + private val flagNotSetSymbol: Char + ) { + + /** + * Returns a character representing whether or not this flag is set in [state]. + * + * A capital character signifies the flag is set and a lowercase character signifies that + * the flag isn't set. + */ + internal fun getFlagStatus(state: Int): Char = + if (0 != state and this.bitMask) this.flagIsSetSymbol + else this.flagNotSetSymbol + } + + /** POJO to hold [disable1] and [disable2]. */ + data class DisableState(val disable1: Int, val disable2: Int) +} + +// LINT.IfChange +private val defaultDisable1FlagsList: List = listOf( + DisableFlagsLogger.DisableFlag(DISABLE_EXPAND, 'E', 'e'), + DisableFlagsLogger.DisableFlag(DISABLE_NOTIFICATION_ICONS, 'N', 'n'), + DisableFlagsLogger.DisableFlag(DISABLE_NOTIFICATION_ALERTS, 'A', 'a'), + DisableFlagsLogger.DisableFlag(DISABLE_SYSTEM_INFO, 'I', 'i'), + DisableFlagsLogger.DisableFlag(DISABLE_HOME, 'H', 'h'), + DisableFlagsLogger.DisableFlag(DISABLE_BACK, 'B', 'b'), + DisableFlagsLogger.DisableFlag(DISABLE_CLOCK, 'C', 'c'), + DisableFlagsLogger.DisableFlag(DISABLE_RECENT, 'R', 'r'), + DisableFlagsLogger.DisableFlag(DISABLE_SEARCH, 'S', 's'), + DisableFlagsLogger.DisableFlag(DISABLE_ONGOING_CALL_CHIP, 'O', 'o') +) + +private val defaultDisable2FlagsList: List = listOf( + DisableFlagsLogger.DisableFlag(DISABLE2_QUICK_SETTINGS, 'Q', 'q'), + DisableFlagsLogger.DisableFlag(DISABLE2_SYSTEM_ICONS, 'I', 'i'), + DisableFlagsLogger.DisableFlag(DISABLE2_NOTIFICATION_SHADE, 'N', 'n'), + DisableFlagsLogger.DisableFlag(DISABLE2_GLOBAL_ACTIONS, 'G', 'g'), + DisableFlagsLogger.DisableFlag(DISABLE2_ROTATE_SUGGESTIONS, 'R', 'r') +) +// LINT.ThenChange(frameworks/base/core/java/android/app/StatusBarManager.java) \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java index 6a510c92a3a08..5301b25715340 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java @@ -57,6 +57,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.qs.QSPanelController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.DisableFlagsLogger; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; @@ -98,6 +99,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks { private final VibratorHelper mVibratorHelper; private final Optional mVibratorOptional; private final LightBarController mLightBarController; + private final DisableFlagsLogger mDisableFlagsLogger; private final int mDisplayId; private final boolean mVibrateOnOpening; private final VibrationEffect mCameraLaunchGestureVibrationEffect; @@ -134,6 +136,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks { VibratorHelper vibratorHelper, Optional vibratorOptional, LightBarController lightBarController, + DisableFlagsLogger disableFlagsLogger, @DisplayId int displayId) { mStatusBar = statusBar; @@ -159,6 +162,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks { mVibratorHelper = vibratorHelper; mVibratorOptional = vibratorOptional; mLightBarController = lightBarController; + mDisableFlagsLogger = disableFlagsLogger; mDisplayId = displayId; mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation); @@ -267,7 +271,17 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks { if (displayId != mDisplayId) { return; } + + int state2BeforeAdjustment = state2; state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2); + Log.d(StatusBar.TAG, + mDisableFlagsLogger.getDisableFlagsString( + /* old= */ new DisableFlagsLogger.DisableState( + mStatusBar.getDisabled1(), mStatusBar.getDisabled2()), + /* new= */ new DisableFlagsLogger.DisableState( + state1, state2BeforeAdjustment), + /* newStateAfterLocalModification= */ new DisableFlagsLogger.DisableState( + state1, state2))); final int old1 = mStatusBar.getDisabled1(); final int diff1 = state1 ^ old1; @@ -277,43 +291,6 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks { final int diff2 = state2 ^ old2; mStatusBar.setDisabled2(state2); - if (StatusBar.DEBUG) { - Log.d(StatusBar.TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)", - old1, state1, diff1)); - Log.d(StatusBar.TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)", - old2, state2, diff2)); - } - - StringBuilder flagdbg = new StringBuilder(); - flagdbg.append("disable<"); - flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_EXPAND)) ? 'E' : 'e'); - flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_EXPAND)) ? '!' : ' '); - flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? 'I' : 'i'); - flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? '!' : ' '); - flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? 'A' : 'a'); - flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? '!' : ' '); - flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? 'S' : 's'); - flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? '!' : ' '); - flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_BACK)) ? 'B' : 'b'); - flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_BACK)) ? '!' : ' '); - flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_HOME)) ? 'H' : 'h'); - flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_HOME)) ? '!' : ' '); - flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_RECENT)) ? 'R' : 'r'); - flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_RECENT)) ? '!' : ' '); - flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_CLOCK)) ? 'C' : 'c'); - flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_CLOCK)) ? '!' : ' '); - flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH)) ? 'S' : 's'); - flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SEARCH)) ? '!' : ' '); - flagdbg.append("> disable2<"); - flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? 'Q' : 'q'); - flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? '!' : ' '); - flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? 'I' : 'i'); - flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? '!' : ' '); - flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? 'N' : 'n'); - flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? '!' : ' '); - flagdbg.append('>'); - Log.d(StatusBar.TAG, flagdbg.toString()); - if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) { if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) { mShadeController.animateCollapsePanels(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt new file mode 100644 index 0000000000000..096efad506152 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertThrows +import org.junit.Test + +@SmallTest +class DisableFlagsLoggerTest : SysuiTestCase() { + private val disable1Flags = listOf( + DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'), + DisableFlagsLogger.DisableFlag(0b010, 'B', 'b'), + DisableFlagsLogger.DisableFlag(0b001, 'C', 'c'), + ) + private val disable2Flags = listOf( + DisableFlagsLogger.DisableFlag(0b10, 'M', 'm'), + DisableFlagsLogger.DisableFlag(0b01, 'N', 'n'), + ) + + private val disableFlagsLogger = DisableFlagsLogger(disable1Flags, disable2Flags) + + @Test + fun getDisableFlagsString_oldAndNewSame_statesLoggedButDiffsNotLogged() { + val state = DisableFlagsLogger.DisableState( + 0b111, // ABC + 0b01 // mN + ) + + val result = disableFlagsLogger.getDisableFlagsString(state, state) + + assertThat(result).contains("Old: ABC.mN") + assertThat(result).contains("New: ABC.mN") + assertThat(result).doesNotContain("(") + assertThat(result).doesNotContain(")") + } + + @Test + fun getDisableFlagsString_oldAndNewDifferent_statesAndDiffLogged() { + val result = disableFlagsLogger.getDisableFlagsString( + DisableFlagsLogger.DisableState( + 0b111, // ABC + 0b01, // mN + ), + DisableFlagsLogger.DisableState( + 0b001, // abC + 0b10 // Mn + ) + ) + + assertThat(result).contains("Old: ABC.mN") + assertThat(result).contains("New: abC.Mn") + assertThat(result).contains("(ab.Mn)") + } + + @Test + fun getDisableFlagsString_onlyDisable2Different_diffLoggedCorrectly() { + val result = disableFlagsLogger.getDisableFlagsString( + DisableFlagsLogger.DisableState( + 0b001, // abC + 0b01, // mN + ), + DisableFlagsLogger.DisableState( + 0b001, // abC + 0b00 // mn + ) + ) + + assertThat(result).contains("(.n)") + } + + @Test + fun getDisableFlagsString_nullLocalModification_localModNotLogged() { + val result = disableFlagsLogger.getDisableFlagsString( + DisableFlagsLogger.DisableState(0, 0), + DisableFlagsLogger.DisableState(1, 1), + newAfterLocalModification = null + ) + + assertThat(result).doesNotContain("local modification") + } + + @Test + fun getDisableFlagsString_newAfterLocalModificationSameAsNew_localModNotLogged() { + val newState = DisableFlagsLogger.DisableState( + 0b001, // abC + 0b10 // mn + ) + + val result = disableFlagsLogger.getDisableFlagsString( + DisableFlagsLogger.DisableState(0, 0), newState, newState + ) + + assertThat(result).doesNotContain("local modification") + } + + @Test + fun getDisableFlagsString_newAfterLocalModificationDifferent_localModAndDiffLogged() { + val result = disableFlagsLogger.getDisableFlagsString( + old = DisableFlagsLogger.DisableState(0, 0), + new = DisableFlagsLogger.DisableState( + 0b000, // abc + 0b00 // mn + ), + newAfterLocalModification = DisableFlagsLogger.DisableState( + 0b100, // Abc + 0b10 // Mn + ) + ) + + assertThat(result).contains("local modification: Abc.Mn (A.M)") + } + + @Test + fun constructor_defaultDisableFlags_noException() { + // Just creating the logger with the default params will trigger the exception if there + // is one. + DisableFlagsLogger() + } + + @Test + fun constructor_disable1_FlagIsSetSymbolNotUnique_exception() { + assertThrows(IllegalArgumentException::class.java) { + DisableFlagsLogger( + disable1FlagsList = listOf( + DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'), + DisableFlagsLogger.DisableFlag(0b010, 'A', 'b'), + ), + listOf() + ) + } + } + + @Test + fun constructor_disable1_FlagNotSetSymbolNotUnique_exception() { + assertThrows(IllegalArgumentException::class.java) { + DisableFlagsLogger( + disable1FlagsList = listOf( + DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'), + DisableFlagsLogger.DisableFlag(0b010, 'B', 'a'), + ), + listOf() + ) + } + } + + @Test + fun constructor_disable2_FlagIsSetSymbolNotUnique_exception() { + assertThrows(IllegalArgumentException::class.java) { + DisableFlagsLogger( + disable1FlagsList = listOf(), + disable2FlagsList = listOf( + DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'), + DisableFlagsLogger.DisableFlag(0b010, 'A', 'b'), + ), + ) + } + } + + @Test + fun constructor_disable2_FlagNotSetSymbolNotUnique_exception() { + assertThrows(IllegalArgumentException::class.java) { + DisableFlagsLogger( + disable1FlagsList = listOf(), + disable2FlagsList = listOf( + DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'), + DisableFlagsLogger.DisableFlag(0b010, 'B', 'a'), + ), + ) + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java index 52538c7f54965..8555306bae043 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java @@ -38,6 +38,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.DisableFlagsLogger; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; @@ -112,6 +113,7 @@ public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase { mVibratorHelper, Optional.of(mVibrator), mLightBarController, + new DisableFlagsLogger(), DEFAULT_DISPLAY); when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);