diff options
67 files changed, 754 insertions, 213 deletions
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java index 34c88e91a979..8da630c95135 100644 --- a/core/java/android/hardware/input/InputSettings.java +++ b/core/java/android/hardware/input/InputSettings.java @@ -28,6 +28,7 @@ import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag; import static com.android.hardware.input.Flags.mouseScrollingAcceleration; import static com.android.hardware.input.Flags.mouseReverseVerticalScrolling; import static com.android.hardware.input.Flags.mouseSwapPrimaryButton; +import static com.android.hardware.input.Flags.pointerAcceleration; import static com.android.hardware.input.Flags.touchpadSystemGestureDisable; import static com.android.hardware.input.Flags.touchpadThreeFingerTapShortcut; import static com.android.hardware.input.Flags.touchpadVisualizer; @@ -418,6 +419,15 @@ public class InputSettings { } /** + * Returns true if the feature flag for the pointer acceleration toggle is + * enabled. + * @hide + */ + public static boolean isPointerAccelerationFeatureFlagEnabled() { + return pointerAcceleration(); + } + + /** * Returns true if the touchpad visualizer is allowed to appear. * * @param context The application context. @@ -720,6 +730,47 @@ public class InputSettings { } /** + * Whether cursor acceleration is enabled or not for connected mice. + * + * @param context The application context. + * + * @hide + */ + public static boolean isMousePointerAccelerationEnabled(@NonNull Context context) { + if (!isPointerAccelerationFeatureFlagEnabled()) { + return false; + } + + return Settings.System.getIntForUser(context.getContentResolver(), + Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED, 1, UserHandle.USER_CURRENT) + == 1; + } + + /** + * Sets whether mouse acceleration is enabled. + * + * When enabled, the mouse cursor moves farther when it is moved faster. + * When disabled, the mouse cursor speed becomes directly proportional to + * the speed at which the mouse is moved. + * + * @param context The application context. + * @param enabled Will enable mouse acceleration if true, disable it if + * false. + * @hide + */ + @RequiresPermission(Manifest.permission.WRITE_SETTINGS) + public static void setMouseAccelerationEnabled(@NonNull Context context, + boolean enabled) { + if (!isPointerAccelerationFeatureFlagEnabled()) { + return; + } + Settings.System.putIntForUser(context.getContentResolver(), + Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED, enabled ? 1 : 0, + UserHandle.USER_CURRENT); + } + + + /** * Whether Accessibility bounce keys feature is enabled. * * <p> diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2ad6669979b7..6e58780ac7a7 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6362,6 +6362,16 @@ public final class Settings { public static final String MOUSE_SCROLLING_ACCELERATION = "mouse_scrolling_acceleration"; /** + * Whether mouse acceleration is enabled. + * + * When enabled, the mouse cursor will accelerate as the mouse moves faster. + * + * @hide + */ + public static final String MOUSE_POINTER_ACCELERATION_ENABLED = + "mouse_pointer_acceleration_enabled"; + + /** * Pointer fill style, specified by * {@link android.view.PointerIcon.PointerIconVectorStyleFill} constants. * @@ -6610,6 +6620,7 @@ public final class Settings { PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE); PRIVATE_SETTINGS.add(MOUSE_REVERSE_VERTICAL_SCROLLING); PRIVATE_SETTINGS.add(MOUSE_SWAP_PRIMARY_BUTTON); + PRIVATE_SETTINGS.add(MOUSE_POINTER_ACCELERATION_ENABLED); PRIVATE_SETTINGS.add(PREFERRED_REGION); PRIVATE_SETTINGS.add(MOUSE_SCROLLING_ACCELERATION); } diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto index dd9bfa51c634..0d99200f4e6f 100644 --- a/core/proto/android/providers/settings/system.proto +++ b/core/proto/android/providers/settings/system.proto @@ -228,6 +228,7 @@ message SystemSettingsProto { optional SettingProto reverse_vertical_scrolling = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto swap_primary_button = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto scrolling_acceleration = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pointer_acceleration_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Mouse mouse = 38; diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java index 7e87462b64de..166b388d9b7b 100644 --- a/media/java/android/media/quality/MediaQualityManager.java +++ b/media/java/android/media/quality/MediaQualityManager.java @@ -276,16 +276,19 @@ public final class MediaQualityManager { /** * Sets preferred default picture profile. * - * @param id the ID of the default profile. {@code null} to unset the default profile. + * @param pictureProfileId the ID of the default profile. {@code null} to unset the default + * profile. * @return {@code true} if it's set successfully; {@code false} otherwise. * + * @see PictureProfile#getProfileId() + * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) - public boolean setDefaultPictureProfile(@Nullable String id) { + public boolean setDefaultPictureProfile(@Nullable String pictureProfileId) { try { - return mService.setDefaultPictureProfile(id, mUserHandle); + return mService.setDefaultPictureProfile(pictureProfileId, mUserHandle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -467,16 +470,19 @@ public final class MediaQualityManager { /** * Sets preferred default sound profile. * - * @param id the ID of the default profile. {@code null} to unset the default profile. + * @param soundProfileId the ID of the default profile. {@code null} to unset the default + * profile. * @return {@code true} if it's set successfully; {@code false} otherwise. * + * @see SoundProfile#getProfileId() + * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE) - public boolean setDefaultSoundProfile(@Nullable String id) { + public boolean setDefaultSoundProfile(@Nullable String soundProfileId) { try { - return mService.setDefaultSoundProfile(id, mUserHandle); + return mService.setDefaultSoundProfile(soundProfileId, mUserHandle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index f1bbfc62681a..5b4ee8bdb339 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -110,6 +110,7 @@ public class SystemSettings { Settings.System.MOUSE_REVERSE_VERTICAL_SCROLLING, Settings.System.MOUSE_SCROLLING_ACCELERATION, Settings.System.MOUSE_SWAP_PRIMARY_BUTTON, + Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED, Settings.System.TOUCHPAD_POINTER_SPEED, Settings.System.TOUCHPAD_NATURAL_SCROLLING, Settings.System.TOUCHPAD_TAP_TO_CLICK, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 6abd9b73e26d..0432eeacec4d 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -226,6 +226,7 @@ public class SystemSettingsValidators { VALIDATORS.put(System.MOUSE_REVERSE_VERTICAL_SCROLLING, BOOLEAN_VALIDATOR); VALIDATORS.put(System.MOUSE_SWAP_PRIMARY_BUTTON, BOOLEAN_VALIDATOR); VALIDATORS.put(System.MOUSE_SCROLLING_ACCELERATION, BOOLEAN_VALIDATOR); + VALIDATORS.put(System.MOUSE_POINTER_ACCELERATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7)); VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR); VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt index f68a1b5a17e9..eae5728f586d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt @@ -16,7 +16,7 @@ package com.android.systemui.display.data.repository -import android.content.testableContext +import android.content.Context import android.platform.test.annotations.EnableFlags import android.view.Display import android.view.layoutInflater @@ -24,6 +24,7 @@ import android.view.mockWindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.SysuiTestableContext import com.android.systemui.display.shared.model.DisplayWindowProperties import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope @@ -36,8 +37,12 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.doAnswer +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.whenever @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) @RunWith(AndroidJUnit4::class) @@ -48,7 +53,8 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { private val fakeDisplayRepository = kosmos.displayRepository private val testScope = kosmos.testScope - private val applicationContext = kosmos.testableContext + private val applicationContext = spy(context) + private val applicationWindowManager = kosmos.mockWindowManager private val applicationLayoutInflater = kosmos.layoutInflater @@ -64,6 +70,22 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { } @Before + fun setUpContext() { + doAnswer { createContextForDisplay(it.arguments[0] as Display) } + .whenever(applicationContext) + .createWindowContext(any(), any(), any()) + } + + private fun createContextForDisplay(display: Display): Context { + if (display.displayId == BEING_REMOVED_DISPLAY_ID) { + // Simulate what happens when a display is being removed. + // Return a context with the same display id as the original context. + return mContext + } + return SysuiTestableContext(mContext).also { it.display = display } + } + + @Before fun start() { repo.start() } @@ -72,6 +94,7 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { fun addDisplays() = runBlocking { fakeDisplayRepository.addDisplay(createDisplay(DEFAULT_DISPLAY_ID)) fakeDisplayRepository.addDisplay(createDisplay(NON_DEFAULT_DISPLAY_ID)) + fakeDisplayRepository.addDisplay(createDisplay(BEING_REMOVED_DISPLAY_ID)) } @Test @@ -94,7 +117,7 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { @Test fun get_nonDefaultDisplayId_returnsNewStatusBarContext() = testScope.runTest { - val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO) + val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)!! assertThat(displayContext.context).isNotSameInstanceAs(applicationContext) } @@ -102,7 +125,7 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { @Test fun get_nonDefaultDisplayId_returnsNewWindowManager() = testScope.runTest { - val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO) + val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)!! assertThat(displayContext.windowManager).isNotSameInstanceAs(applicationWindowManager) } @@ -110,7 +133,7 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { @Test fun get_nonDefaultDisplayId_returnsNewLayoutInflater() = testScope.runTest { - val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO) + val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)!! assertThat(displayContext.layoutInflater).isNotSameInstanceAs(applicationLayoutInflater) } @@ -154,17 +177,26 @@ class DisplayWindowPropertiesRepositoryImplTest : SysuiTestCase() { .isNotSameInstanceAs(displayContext) } - @Test(expected = IllegalArgumentException::class) - fun get_nonExistingDisplayId_throws() = - testScope.runTest { repo.get(NON_EXISTING_DISPLAY_ID, WINDOW_TYPE_FOO) } + @Test + fun get_nonExistingDisplayId_returnsNull() = + testScope.runTest { + assertThat(repo.get(NON_EXISTING_DISPLAY_ID, WINDOW_TYPE_FOO)).isNull() + } + + @Test + fun get_displayBeingRemoved_returnsNull() = + testScope.runTest { + assertThat(repo.get(BEING_REMOVED_DISPLAY_ID, WINDOW_TYPE_FOO)).isNull() + } private fun createDisplay(displayId: Int) = - mock<Display> { on { getDisplayId() } doReturn displayId } + mock<Display> { on { getDisplayId() } doReturn (displayId) } companion object { private const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY private const val NON_DEFAULT_DISPLAY_ID = DEFAULT_DISPLAY_ID + 1 private const val NON_EXISTING_DISPLAY_ID = DEFAULT_DISPLAY_ID + 2 + private const val BEING_REMOVED_DISPLAY_ID = DEFAULT_DISPLAY_ID + 4 private const val WINDOW_TYPE_FOO = 123 private const val WINDOW_TYPE_BAR = 321 } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt index 6a0781b3580f..73957eb488d0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt @@ -80,9 +80,9 @@ class PerDisplayStoreImplTest : SysuiTestCase() { assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(instance) } - @Test(expected = IllegalArgumentException::class) - fun forDisplay_nonExistingDisplayId_throws() = - testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) } + @Test + fun forDisplay_nonExistingDisplayId_returnsNull() = + testScope.runTest { assertThat(store.forDisplay(NON_EXISTING_DISPLAY_ID)).isNull() } @Test fun forDisplay_afterDisplayRemoved_onDisplayRemovalActionInvoked() = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt index 009b33b9f808..3515c5649ca0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt @@ -26,10 +26,12 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.fragments.FragmentHostManager import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.plugins.fakeDarkIconDispatcher import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent import com.android.systemui.statusbar.pipeline.shared.ui.composable.StatusBarRootFactory +import com.android.systemui.statusbar.policy.statusBarConfigurationController import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.testKosmos @@ -77,6 +79,8 @@ class StatusBarInitializerTest : SysuiTestCase() { componentFactory = mock(HomeStatusBarComponent.Factory::class.java), creationListeners = setOf(), statusBarModePerDisplayRepository = statusBarModePerDisplayRepository, + darkIconDispatcher = kosmos.fakeDarkIconDispatcher, + statusBarConfigurationController = kosmos.statusBarConfigurationController, ) @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt index 18eef33813f6..884c35c3457d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt @@ -51,7 +51,7 @@ class LightBarControllerStoreImplTest : SysuiTestCase() { @Test fun forDisplay_startsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance).start() } @@ -59,7 +59,7 @@ class LightBarControllerStoreImplTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -67,7 +67,7 @@ class LightBarControllerStoreImplTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt index a2c3c66f4448..f37648a639df 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt @@ -56,7 +56,7 @@ class MultiDisplayDarkIconDispatcherStoreTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -64,7 +64,7 @@ class MultiDisplayDarkIconDispatcherStoreTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt index 4a26fdf20e6e..e0a1f273aa44 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt @@ -51,7 +51,7 @@ class MultiDisplayStatusBarContentInsetsProviderStoreTest : SysuiTestCase() { @Test fun forDisplay_startsInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance).start() } @@ -59,7 +59,7 @@ class MultiDisplayStatusBarContentInsetsProviderStoreTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -67,7 +67,7 @@ class MultiDisplayStatusBarContentInsetsProviderStoreTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt index a9920ec5e29b..11fd902fc50c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt @@ -53,7 +53,7 @@ class MultiDisplayStatusBarModeRepositoryStoreTest : SysuiTestCase() { @Test fun forDisplay_startsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance).start() } @@ -61,7 +61,7 @@ class MultiDisplayStatusBarModeRepositoryStoreTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt index e65c04c45382..3cc592c94678 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt @@ -56,7 +56,7 @@ class SystemEventChipAnimationControllerStoreImplTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -64,7 +64,7 @@ class SystemEventChipAnimationControllerStoreImplTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt index be20bc1bf9d4..d86c6efce284 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt @@ -225,7 +225,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati val displayId = 123 darkIconRepository.darkState(displayId).value = SysuiDarkIconDispatcher.DarkChange(emptyList(), 0f, 0xAABBCC) - val iconColors by collectLastValue(underTest.iconColors(displayId)) + val iconColors by collectLastValue(underTest.iconColors(displayId)!!) assertThat(iconColors).isNotNull() assertThat(iconColors!!.tint).isEqualTo(0xAABBCC) @@ -241,7 +241,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati val displayId = 321 darkIconRepository.darkState(displayId).value = SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC) - val iconColors by collectLastValue(underTest.iconColors(displayId)) + val iconColors by collectLastValue(underTest.iconColors(displayId)!!) val staticDrawableColor = iconColors?.staticDrawableColor(Rect(6, 6, 7, 7)) assertThat(staticDrawableColor).isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT) } @@ -252,7 +252,7 @@ class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterizati val displayId = 987 darkIconRepository.darkState(displayId).value = SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC) - val iconColors by collectLastValue(underTest.iconColors(displayId)) + val iconColors by collectLastValue(underTest.iconColors(displayId)!!) assertThat(iconColors!!.staticDrawableColor(Rect(6, 6, 7, 7))) .isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt index 90506a1b9a7f..d16372611e88 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt @@ -56,7 +56,7 @@ class MultiDisplayAutoHideControllerStoreTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -64,7 +64,7 @@ class MultiDisplayAutoHideControllerStoreTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt index 2d9880a48f80..659d91a95aa8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt @@ -39,7 +39,7 @@ class LightsOutInteractorTest : SysuiTestCase() { fun isLowProfile_lightsOutStatusBarMode_false() = runTest { statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.LIGHTS_OUT - val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)!!) assertThat(actual).isTrue() } @@ -49,7 +49,7 @@ class LightsOutInteractorTest : SysuiTestCase() { statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.LIGHTS_OUT_TRANSPARENT - val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)!!) assertThat(actual).isTrue() } @@ -58,7 +58,7 @@ class LightsOutInteractorTest : SysuiTestCase() { fun isLowProfile_transparentStatusBarMode_false() = runTest { statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT - val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)) + val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)!!) assertThat(actual).isFalse() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt index 7a9d0179b239..769f012bfdf7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt @@ -53,7 +53,7 @@ class MultiDisplayStatusBarWindowControllerStoreTest : SysuiTestCase() { @Test fun beforeDisplayRemoved_doesNotStopInstances() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! verify(instance, never()).stop() } @@ -61,7 +61,7 @@ class MultiDisplayStatusBarWindowControllerStoreTest : SysuiTestCase() { @Test fun displayRemoved_stopsInstance() = testScope.runTest { - val instance = underTest.forDisplay(DEFAULT_DISPLAY) + val instance = underTest.forDisplay(DEFAULT_DISPLAY)!! fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt index 8972f3ec71af..8b526bb1d55b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt @@ -30,12 +30,14 @@ import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecogn import com.android.systemui.touchpad.ui.gesture.FakeVelocityTracker import com.google.common.truth.Truth.assertThat import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @SmallTest +@Ignore("b/386412866") @RunWith(ParameterizedAndroidJunit4::class) class ThreeFingerGestureRecognizerTest( private val recognizer: GestureRecognizer, diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt index f310b30c1a81..3390640fa6c6 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt @@ -18,6 +18,8 @@ package com.android.systemui.display.data.repository import android.annotation.SuppressLint import android.content.Context +import android.os.Bundle +import android.util.Log import android.view.Display import android.view.LayoutInflater import android.view.WindowManager @@ -39,14 +41,13 @@ import kotlinx.coroutines.CoroutineScope interface DisplayWindowPropertiesRepository { /** - * Returns a [DisplayWindowProperties] instance for a given display id and window type. - * - * @throws IllegalArgumentException if no display with the given display id exists. + * Returns a [DisplayWindowProperties] instance for a given display id and window type, or null + * if no display with the given display id exists. */ fun get( displayId: Int, @WindowManager.LayoutParams.WindowType windowType: Int, - ): DisplayWindowProperties + ): DisplayWindowProperties? } @SysUISingleton @@ -72,12 +73,10 @@ constructor( override fun get( displayId: Int, @WindowManager.LayoutParams.WindowType windowType: Int, - ): DisplayWindowProperties { - val display = - displayRepository.getDisplay(displayId) - ?: throw IllegalArgumentException("Display with id $displayId doesn't exist") + ): DisplayWindowProperties? { + val display = displayRepository.getDisplay(displayId) ?: return null return properties.get(displayId, windowType) - ?: create(display, windowType).also { properties.put(displayId, windowType, it) } + ?: create(display, windowType)?.also { properties.put(displayId, windowType, it) } } override fun start() { @@ -88,7 +87,7 @@ constructor( } } - private fun create(display: Display, windowType: Int): DisplayWindowProperties { + private fun create(display: Display, windowType: Int): DisplayWindowProperties? { val displayId = display.displayId return if (displayId == Display.DEFAULT_DISPLAY) { // For the default display, we can just reuse the global/application properties. @@ -102,6 +101,14 @@ constructor( ) } else { val context = createWindowContext(display, windowType) + if (context.displayId != display.displayId) { + Log.e( + TAG, + "Returning null because the new context doesn't have the desired display id " + + "${display.displayId}. Display was already removed.", + ) + return null + } @SuppressLint("NonInjectedService") // Need to manually get the service val windowManager = context.getSystemService(WindowManager::class.java) as WindowManager val layoutInflater = LayoutInflater.from(context) @@ -110,11 +117,15 @@ constructor( } private fun createWindowContext(display: Display, windowType: Int): Context = - globalContext.createWindowContext(display, windowType, /* options= */ null).also { + globalContext.createWindowContext(display, windowType, /* options= */ Bundle.EMPTY).also { it.setTheme(R.style.Theme_SystemUI) } override fun dump(pw: PrintWriter, args: Array<out String>) { pw.write("perDisplayContexts: $properties") } + + private companion object { + const val TAG = "DisplayWindowPropsRepo" + } } diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt index 711534f9dbf0..564588c159bd 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt @@ -16,6 +16,7 @@ package com.android.systemui.display.data.repository +import android.util.Log import android.view.Display import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable @@ -36,12 +37,10 @@ interface PerDisplayStore<T> { val defaultDisplay: T /** - * Returns an instance for a specific display id. - * - * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing - * displays. + * Returns an instance for a specific display id, or null if [displayId] doesn't match the id of + * any existing displays. */ - fun forDisplay(displayId: Int): T + fun forDisplay(displayId: Int): T? } abstract class PerDisplayStoreImpl<T>( @@ -58,7 +57,7 @@ abstract class PerDisplayStoreImpl<T>( * Note that the id of the default display is [Display.DEFAULT_DISPLAY]. */ override val defaultDisplay: T - get() = forDisplay(Display.DEFAULT_DISPLAY) + get() = forDisplay(Display.DEFAULT_DISPLAY)!! /** * Returns an instance for a specific display id. @@ -66,16 +65,30 @@ abstract class PerDisplayStoreImpl<T>( * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing * displays. */ - override fun forDisplay(displayId: Int): T { + override fun forDisplay(displayId: Int): T? { if (displayRepository.getDisplay(displayId) == null) { - throw IllegalArgumentException("Display with id $displayId doesn't exist.") + Log.e(TAG, "<${instanceClass.simpleName}>: Display with id $displayId doesn't exist.") + return null } - return perDisplayInstances.computeIfAbsent(displayId) { - createInstanceForDisplay(displayId) + synchronized(perDisplayInstances) { + val existingInstance = perDisplayInstances[displayId] + if (existingInstance != null) { + return existingInstance + } + val newInstance = createInstanceForDisplay(displayId) + if (newInstance == null) { + Log.e( + TAG, + "<${instanceClass.simpleName}> returning null because createInstanceForDisplay($displayId) returned null.", + ) + } else { + perDisplayInstances[displayId] = newInstance + } + return newInstance } } - protected abstract fun createInstanceForDisplay(displayId: Int): T + protected abstract fun createInstanceForDisplay(displayId: Int): T? override fun start() { val instanceType = instanceClass.simpleName @@ -98,6 +111,10 @@ abstract class PerDisplayStoreImpl<T>( override fun dump(pw: PrintWriter, args: Array<out String>) { pw.println(perDisplayInstances) } + + private companion object { + const val TAG = "PerDisplayStore" + } } class SingleDisplayStore<T>(defaultInstance: T) : PerDisplayStore<T> { diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt index 22e467bd5e3c..99c9ca98bae9 100644 --- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt @@ -33,7 +33,7 @@ interface DisplayWindowPropertiesInteractor { * * @throws IllegalArgumentException if no display with the given display id exists. */ - fun getForStatusBar(displayId: Int): DisplayWindowProperties + fun getForStatusBar(displayId: Int): DisplayWindowProperties? } @SysUISingleton @@ -42,7 +42,7 @@ class DisplayWindowPropertiesInteractorImpl constructor(private val repo: DisplayWindowPropertiesRepository) : DisplayWindowPropertiesInteractor { - override fun getForStatusBar(displayId: Int): DisplayWindowProperties { + override fun getForStatusBar(displayId: Int): DisplayWindowProperties? { return repo.get(displayId, TYPE_STATUS_BAR) } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java index c895732f79f6..f9df67661b49 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java @@ -720,9 +720,24 @@ public class NavigationBar extends ViewController<NavigationBarView> implements if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView); - mViewCaptureAwareWindowManager.addView(mFrame, - getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration - .getRotation())); + try { + mViewCaptureAwareWindowManager.addView( + mFrame, + getBarLayoutParams( + mContext.getResources() + .getConfiguration() + .windowConfiguration + .getRotation())); + } catch (WindowManager.InvalidDisplayException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + Log.e( + TAG, + "Unable to add view to WindowManager. Display with id " + + mDisplayId + + " does not exist anymore", + e); + } mDisplayId = mContext.getDisplayId(); mIsOnDefaultDisplay = mDisplayId == mDisplayTracker.getDefaultDisplayId(); @@ -764,6 +779,15 @@ public class NavigationBar extends ViewController<NavigationBarView> implements Trace.beginSection("NavigationBar#removeViewImmediate"); try { mViewCaptureAwareWindowManager.removeViewImmediate(mView.getRootView()); + } catch (IllegalArgumentException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + // When that happens, adding the View to WindowManager fails, and therefore removing + // it here will fail too, since it wasn't added in the first place. + Log.e( + TAG, + "Failed to removed view from WindowManager. The View wasn't attached.", + e); } finally { Trace.endSection(); } @@ -859,7 +883,15 @@ public class NavigationBar extends ViewController<NavigationBarView> implements if (mOrientationHandle != null) { resetSecondaryHandle(); getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener); - mViewCaptureAwareWindowManager.removeView(mOrientationHandle); + try { + mViewCaptureAwareWindowManager.removeView(mOrientationHandle); + } catch (IllegalArgumentException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + // When that happens, adding the View to WindowManager fails, and therefore removing + // it here will fail too, since it wasn't added in the first place. + Log.e(TAG, "Trying to remove a View that is not attached", e); + } mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener( mOrientationHandleGlobalLayoutListener); } @@ -930,7 +962,18 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId()); mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; - mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams); + try { + mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams); + } catch (WindowManager.InvalidDisplayException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + Log.e( + TAG, + "Unable to add view to WindowManager. Display with id " + + mDisplayId + + " does not exist anymore", + e); + } mOrientationHandle.setVisibility(View.GONE); logNavbarOrientation("initSecondaryHomeHandleForRotation"); diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt index 201dc0339a0a..4edba2785e89 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt @@ -77,7 +77,17 @@ constructor( private fun getContextOrDefault(displayId: Int): Context { return try { traceSection({ "Getting dialog context for displayId=$displayId" }) { - displayWindowPropertyRepository.get().get(displayId, DIALOG_WINDOW_TYPE).context + val displayWindowProperties = + displayWindowPropertyRepository.get().get(displayId, DIALOG_WINDOW_TYPE) + if (displayWindowProperties == null) { + Log.e( + TAG, + "DisplayWindowPropertiesRepository returned null for display $displayId. Returning default one", + ) + defaultContext + } else { + displayWindowProperties.context + } } } catch (e: Exception) { // This can happen if the display was disconnected in the meantime. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt index d24eddaf321f..d25ca285c53b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt @@ -72,7 +72,7 @@ constructor( private fun initializeStatusBarForDisplay(displayId: Int, result: RegisterStatusBarResult) { if ((result.mTransientBarTypes and WindowInsets.Type.statusBars()) != 0) { - statusBarModeRepository.forDisplay(displayId).showTransient() + statusBarModeRepository.forDisplay(displayId)?.showTransient() } val commandQueueCallbacks = commandQueueCallbacksLazy.get() commandQueueCallbacks.onSystemBarAttributesChanged( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt index 9e9a38e87924..b057fb0433fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt @@ -89,21 +89,26 @@ constructor( } private fun createAndStartOrchestratorForDisplay(displayId: Int) { + val statusBarModeRepository = statusBarModeRepositoryStore.forDisplay(displayId) ?: return + val statusBarInitializer = initializerStore.forDisplay(displayId) ?: return + val statusBarWindowController = + statusBarWindowControllerStore.forDisplay(displayId) ?: return + val autoHideController = autoHideControllerStore.forDisplay(displayId) ?: return statusBarOrchestratorFactory .create( displayId, displayScopeRepository.scopeForDisplay(displayId), statusBarWindowStateRepositoryStore.forDisplay(displayId), - statusBarModeRepositoryStore.forDisplay(displayId), - initializerStore.forDisplay(displayId), - statusBarWindowControllerStore.forDisplay(displayId), - autoHideControllerStore.forDisplay(displayId), + statusBarModeRepository, + statusBarInitializer, + statusBarWindowController, + autoHideController, ) .start() } private fun createAndStartInitializerForDisplay(displayId: Int) { - statusBarInitializerStore.forDisplay(displayId).start() + statusBarInitializerStore.forDisplay(displayId)?.start() } private fun startPrivacyDotForDisplay(displayId: Int) { @@ -111,6 +116,6 @@ constructor( // For the default display, privacy dot is started via ScreenDecorations return } - privacyDotWindowControllerStore.forDisplay(displayId).start() + privacyDotWindowControllerStore.forDisplay(displayId)?.start() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt index 4c54fc49e536..1e127ee054e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt @@ -20,9 +20,11 @@ import android.view.ViewGroup import androidx.annotation.VisibleForTesting import com.android.systemui.CoreStartable import com.android.systemui.fragments.FragmentHostManager +import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.res.R import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions import com.android.systemui.statusbar.phone.PhoneStatusBarView @@ -34,7 +36,6 @@ import com.android.systemui.statusbar.window.StatusBarWindowController import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import java.lang.IllegalStateException import javax.inject.Provider /** @@ -75,6 +76,8 @@ interface StatusBarInitializer : CoreStartable { fun create( statusBarWindowController: StatusBarWindowController, statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository, + statusBarConfigurationController: StatusBarConfigurationController, + darkIconDispatcher: DarkIconDispatcher, ): StatusBarInitializer } } @@ -84,6 +87,8 @@ class StatusBarInitializerImpl constructor( @Assisted private val statusBarWindowController: StatusBarWindowController, @Assisted private val statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository, + @Assisted private val statusBarConfigurationController: StatusBarConfigurationController, + @Assisted private val darkIconDispatcher: DarkIconDispatcher, private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>, private val statusBarRootFactory: StatusBarRootFactory, private val componentFactory: HomeStatusBarComponent.Factory, @@ -131,23 +136,32 @@ constructor( -> val phoneStatusBarView = cv.findViewById<PhoneStatusBarView>(R.id.status_bar) component = - componentFactory.create(phoneStatusBarView).also { component -> - // CollapsedStatusBarFragment used to be responsible initializing - component.init() - - statusBarViewUpdatedListener?.onStatusBarViewUpdated( - component.phoneStatusBarViewController, - component.phoneStatusBarTransitions, + componentFactory + .create( + phoneStatusBarView, + statusBarConfigurationController, + statusBarWindowController, + darkIconDispatcher, ) - - if (StatusBarConnectedDisplays.isEnabled) { - statusBarModePerDisplayRepository.onStatusBarViewInitialized(component) - } else { - creationListeners.forEach { listener -> - listener.onStatusBarViewInitialized(component) + .also { component -> + // CollapsedStatusBarFragment used to be responsible initializing + component.init() + + statusBarViewUpdatedListener?.onStatusBarViewUpdated( + component.phoneStatusBarViewController, + component.phoneStatusBarTransitions, + ) + + if (StatusBarConnectedDisplays.isEnabled) { + statusBarModePerDisplayRepository.onStatusBarViewInitialized( + component + ) + } else { + creationListeners.forEach { listener -> + listener.onStatusBarViewInitialized(component) + } } } - } } // Add the new compose view to the hierarchy because we don't use fragment transactions @@ -163,9 +177,11 @@ constructor( CollapsedStatusBarFragment.TAG, object : FragmentHostManager.FragmentListener { override fun onFragmentViewCreated(tag: String, fragment: Fragment) { - component = - (fragment as CollapsedStatusBarFragment).homeStatusBarComponent - ?: throw IllegalStateException() + val statusBarFragment = fragment as CollapsedStatusBarFragment + if (statusBarFragment.homeStatusBarComponent == null) { + return + } + component = fragment.homeStatusBarComponent statusBarViewUpdatedListener?.onStatusBarViewUpdated( component!!.phoneStatusBarViewController, component!!.phoneStatusBarTransitions, @@ -195,6 +211,8 @@ constructor( override fun create( statusBarWindowController: StatusBarWindowController, statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository, + statusBarConfigurationController: StatusBarConfigurationController, + darkIconDispatcher: DarkIconDispatcher, ): StatusBarInitializerImpl } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt index 4f815c1f0b31..de6cd072afd7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt @@ -22,6 +22,8 @@ import com.android.systemui.display.data.repository.DisplayRepository import com.android.systemui.display.data.repository.PerDisplayStore import com.android.systemui.display.data.repository.PerDisplayStoreImpl import com.android.systemui.display.data.repository.SingleDisplayStore +import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import javax.inject.Inject @@ -39,6 +41,8 @@ constructor( private val factory: StatusBarInitializer.Factory, private val statusBarWindowControllerStore: StatusBarWindowControllerStore, private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore, + private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, + private val darkIconDispatcherStore: DarkIconDispatcherStore, ) : StatusBarInitializerStore, PerDisplayStoreImpl<StatusBarInitializer>(backgroundApplicationScope, displayRepository) { @@ -47,10 +51,19 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): StatusBarInitializer { + override fun createInstanceForDisplay(displayId: Int): StatusBarInitializer? { + val statusBarWindowController = + statusBarWindowControllerStore.forDisplay(displayId) ?: return null + val statusBarModePerDisplayRepository = + statusBarModeRepositoryStore.forDisplay(displayId) ?: return null + val statusBarConfigurationController = + statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null + val darkIconDispatcher = darkIconDispatcherStore.forDisplay(displayId) ?: return null return factory.create( - statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId), - statusBarModePerDisplayRepository = statusBarModeRepositoryStore.forDisplay(displayId), + statusBarWindowController, + statusBarModePerDisplayRepository, + statusBarConfigurationController, + darkIconDispatcher, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt index 8183a487cee2..041f3c816149 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt @@ -65,8 +65,9 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): SysuiDarkIconDispatcher { - val properties = displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) + override fun createInstanceForDisplay(displayId: Int): SysuiDarkIconDispatcher? { + val properties = + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null return factory.create(displayId, properties.context) } @@ -103,7 +104,7 @@ constructor(private val store: SysuiDarkIconDispatcherStore) : DarkIconDispatche override val defaultDisplay: DarkIconDispatcher get() = store.defaultDisplay - override fun forDisplay(displayId: Int): DarkIconDispatcher = store.forDisplay(displayId) + override fun forDisplay(displayId: Int): DarkIconDispatcher? = store.forDisplay(displayId) } @Module diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt index e4987555833b..c629d10b90b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt @@ -49,13 +49,16 @@ constructor( LightBarControllerStore, PerDisplayStoreImpl<LightBarController>(backgroundApplicationScope, displayRepository) { - override fun createInstanceForDisplay(displayId: Int): LightBarController { + override fun createInstanceForDisplay(displayId: Int): LightBarController? { + val darkIconDispatcher = darkIconDispatcherStore.forDisplay(displayId) ?: return null + val statusBarModePerDisplayRepository = + statusBarModeRepositoryStore.forDisplay(displayId) ?: return null return factory .create( displayId, displayScopeRepository.scopeForDisplay(displayId), - darkIconDispatcherStore.forDisplay(displayId), - statusBarModeRepositoryStore.forDisplay(displayId), + darkIconDispatcher, + statusBarModePerDisplayRepository, ) .also { it.start() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt index bd61c44e3d9b..d48c94bd0893 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt @@ -52,11 +52,14 @@ constructor( PrivacyDotViewControllerStore, PerDisplayStoreImpl<PrivacyDotViewController>(backgroundApplicationScope, displayRepository) { - override fun createInstanceForDisplay(displayId: Int): PrivacyDotViewController { + override fun createInstanceForDisplay(displayId: Int): PrivacyDotViewController? { + val configurationController = + statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null + val contentInsetsProvider = contentInsetsProviderStore.forDisplay(displayId) ?: return null return factory.create( displayScopeRepository.scopeForDisplay(displayId), - statusBarConfigurationControllerStore.forDisplay(displayId), - contentInsetsProviderStore.forDisplay(displayId), + configurationController, + contentInsetsProvider, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt index a1f56552629b..086cc99957ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt @@ -58,15 +58,18 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): PrivacyDotWindowController { + override fun createInstanceForDisplay(displayId: Int): PrivacyDotWindowController? { if (displayId == Display.DEFAULT_DISPLAY) { throw IllegalArgumentException("This class should only be used for connected displays") } val displayWindowProperties = displayWindowPropertiesRepository.get(displayId, TYPE_NAVIGATION_BAR_PANEL) + ?: return null + val privacyDotViewController = + privacyDotViewControllerStore.forDisplay(displayId) ?: return null return windowControllerFactory.create( displayId = displayId, - privacyDotViewController = privacyDotViewControllerStore.forDisplay(displayId), + privacyDotViewController = privacyDotViewController, viewCaptureAwareWindowManager = viewCaptureAwareWindowManagerFactory.create(displayWindowProperties.windowManager), inflater = displayWindowProperties.layoutInflater, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt index 6cf2c73a7138..38cea832ad76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt @@ -62,9 +62,9 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): StatusBarConfigurationController { + override fun createInstanceForDisplay(displayId: Int): StatusBarConfigurationController? { val displayWindowProperties = - displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null return configurationControllerFactory.create(displayWindowProperties.context) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt index e471b12c1d58..554c46f6c219 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt @@ -59,13 +59,17 @@ constructor( displayRepository, ) { - override fun createInstanceForDisplay(displayId: Int): StatusBarContentInsetsProvider { - val context = displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR).context + override fun createInstanceForDisplay(displayId: Int): StatusBarContentInsetsProvider? { + val displayWindowProperties = + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null + val context = displayWindowProperties.context + val configurationController = + statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null val cameraProtectionLoader = cameraProtectionLoaderFactory.create(context) return factory .create( context, - statusBarConfigurationControllerStore.forDisplay(displayId), + configurationController, sysUICutoutProviderFactory.create(context, cameraProtectionLoader), ) .also { it.start() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt index 7760f58805c9..ffc125539521 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt @@ -62,11 +62,17 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): SystemEventChipAnimationController { + override fun createInstanceForDisplay(displayId: Int): SystemEventChipAnimationController? { + val displayWindowProperties = + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null + val statusBarWindowController = + statusBarWindowControllerStore.forDisplay(displayId) ?: return null + val contentInsetsProvider = + statusBarContentInsetsProviderStore.forDisplay(displayId) ?: return null return factory.create( - displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR).context, - statusBarWindowControllerStore.forDisplay(displayId), - statusBarContentInsetsProviderStore.forDisplay(displayId), + displayWindowProperties.context, + statusBarWindowController, + contentInsetsProvider, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt index f2bb7b16439d..4b9721ea4fe5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt @@ -72,5 +72,5 @@ constructor( } private fun controllersForAllDisplays() = - displayRepository.displays.value.map { controllerStore.forDisplay(it.displayId) } + displayRepository.displays.value.mapNotNull { controllerStore.forDisplay(it.displayId) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt index 9928ac67f185..f7799bb75ae0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.events +import android.util.Log import android.view.Display import android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM import android.view.DisplayCutout.BOUNDS_POSITION_LEFT @@ -23,6 +24,7 @@ import android.view.DisplayCutout.BOUNDS_POSITION_RIGHT import android.view.DisplayCutout.BOUNDS_POSITION_TOP import android.view.LayoutInflater import android.view.View +import android.view.WindowManager.InvalidDisplayException import android.view.WindowManager.LayoutParams.WRAP_CONTENT import android.widget.FrameLayout import com.android.app.viewcapture.ViewCaptureAwareWindowManager @@ -97,7 +99,17 @@ constructor( // PrivacyDotViewController expects the dot view to have a FrameLayout parent. val rootView = FrameLayout(context) rootView.addView(this) - viewCaptureAwareWindowManager.addView(rootView, params) + try { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + viewCaptureAwareWindowManager.addView(rootView, params) + } catch (e: InvalidDisplayException) { + Log.e( + TAG, + "Unable to add view to WM. Display with id $displayId does not exist anymore", + e, + ) + } } @AssistedFactory @@ -109,4 +121,8 @@ constructor( inflater: LayoutInflater, ): PrivacyDotWindowController } + + private companion object { + const val TAG = "PrivacyDotWindowController" + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt index 227a1fefb982..eb55856d994b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt @@ -62,8 +62,10 @@ constructor( override fun iconView(key: String): StatusBarIconView? { val entry = notifCollection.getEntry(key) ?: return null + val displayWindowProperties = + displayWindowPropertiesInteractor.getForStatusBar(displayId) ?: return null return cachedIcons.computeIfAbsent(key) { - val context = displayWindowPropertiesInteractor.getForStatusBar(displayId).context + val context = displayWindowProperties.context iconManager.createSbIconView(context, entry) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt index 2ba28a660116..e1032820fb71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt @@ -68,13 +68,14 @@ constructor( .distinctUntilChanged() /** The colors with which to display the notification icons. */ - fun iconColors(displayId: Int): Flow<NotificationIconColors> = - darkIconInteractor + fun iconColors(displayId: Int): Flow<NotificationIconColors> { + return darkIconInteractor .darkState(displayId) .map { (areas: Collection<Rect>, tint: Int) -> IconColorsImpl(tint, areas) } .flowOn(bgContext) .conflate() .distinctUntilChanged() + } /** [NotificationIconsViewData] indicating which icons to display in the view. */ val icons: Flow<NotificationIconsViewData> = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt index 744f96918eef..2ae38dd488bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt @@ -47,9 +47,9 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): AutoHideController { + override fun createInstanceForDisplay(displayId: Int): AutoHideController? { val displayWindowProperties = - displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) + displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null return autoHideControllerFactory.create(displayWindowProperties.context) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java index ea67f1cdb60a..ca0c1ac9ce7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java @@ -499,9 +499,9 @@ public class LightBarControllerImpl implements /** Creates a {@link LightBarControllerImpl}. */ LightBarControllerImpl create( int displayId, - CoroutineScope coroutineScope, - DarkIconDispatcher darkIconDispatcher, - StatusBarModePerDisplayRepository statusBarModePerDisplayRepository); + @NonNull CoroutineScope coroutineScope, + @NonNull DarkIconDispatcher darkIconDispatcher, + @NonNull StatusBarModePerDisplayRepository statusBarModePerDisplayRepository); } public static class LegacyFactory implements LightBarController.Factory { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt index 394502b2d31f..031754db3c57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt @@ -33,6 +33,7 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.res.R +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange @@ -54,7 +55,7 @@ constructor( ) { /** Creates listener always using the same light color for overlay */ - fun createListener(view: View) = + fun createListener(view: View): StatusOverlayHoverListener = StatusOverlayHoverListener( view, configurationController, @@ -65,8 +66,10 @@ constructor( /** * Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay */ - fun createDarkAwareListener(view: View) = - createDarkAwareListener(view, view.darkIconDispatcher.darkChangeFlow()) + fun createDarkAwareListener(view: View): StatusOverlayHoverListener? { + val darkIconDispatcher = view.darkIconDispatcher ?: return null + return createDarkAwareListener(view, darkIconDispatcher.darkChangeFlow()) + } /** * Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay @@ -78,27 +81,34 @@ constructor( rightHoverMargin: Int = 0, topHoverMargin: Int = 0, bottomHoverMargin: Int = 0, - ) = - createDarkAwareListener( + ): StatusOverlayHoverListener? { + val darkIconDispatcher = view.darkIconDispatcher ?: return null + return createDarkAwareListener( view, - view.darkIconDispatcher.darkChangeFlow(), + darkIconDispatcher.darkChangeFlow(), leftHoverMargin, rightHoverMargin, topHoverMargin, bottomHoverMargin, ) + } /** * Creates listener using provided [DarkChange] producer to determine light or dark color of the * overlay */ - fun createDarkAwareListener(view: View, darkFlow: StateFlow<DarkChange>) = - StatusOverlayHoverListener( + fun createDarkAwareListener( + view: View, + darkFlow: StateFlow<DarkChange>, + ): StatusOverlayHoverListener? { + val configurationController = view.statusBarConfigurationController ?: return null + return StatusOverlayHoverListener( view, - view.statusBarConfigurationController, + configurationController, view.resources, darkFlow.map { toHoverTheme(view, it) }, ) + } private fun createDarkAwareListener( view: View, @@ -107,10 +117,11 @@ constructor( rightHoverMargin: Int = 0, topHoverMargin: Int = 0, bottomHoverMargin: Int = 0, - ) = - StatusOverlayHoverListener( + ): StatusOverlayHoverListener? { + val configurationController = view.statusBarConfigurationController ?: return null + return StatusOverlayHoverListener( view, - view.statusBarConfigurationController, + configurationController, view.resources, darkFlow.map { toHoverTheme(view, it) }, leftHoverMargin, @@ -118,11 +129,12 @@ constructor( topHoverMargin, bottomHoverMargin, ) + } - private val View.statusBarConfigurationController + private val View.statusBarConfigurationController: StatusBarConfigurationController? get() = statusBarConfigurationControllerStore.forDisplay(context.displayId) - private val View.darkIconDispatcher + private val View.darkIconDispatcher: SysuiDarkIconDispatcher? get() = darkIconDispatcherStore.forDisplay(context.displayId) private fun toHoverTheme(view: View, darkChange: DarkChange): HoverTheme { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt index 4f32aaa2654e..037dda91fb00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt @@ -31,8 +31,10 @@ import com.android.systemui.statusbar.core.StatusBarInitializerImpl import com.android.systemui.statusbar.core.StatusBarInitializerStore import com.android.systemui.statusbar.core.StatusBarOrchestrator import com.android.systemui.statusbar.core.StatusBarRootModernization +import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerStoreModule import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStoreModule +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.events.PrivacyDotViewControllerModule import com.android.systemui.statusbar.phone.AutoHideControllerStore @@ -107,10 +109,14 @@ interface StatusBarPhoneModule { implFactory: StatusBarInitializerImpl.Factory, statusBarWindowControllerStore: StatusBarWindowControllerStore, statusBarModeRepositoryStore: StatusBarModeRepositoryStore, + statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, + darkIconDispatcherStore: DarkIconDispatcherStore, ): StatusBarInitializerImpl { return implFactory.create( statusBarWindowControllerStore.defaultDisplay, statusBarModeRepositoryStore.defaultDisplay, + statusBarConfigurationControllerStore.defaultDisplay, + darkIconDispatcherStore.defaultDisplay, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt index 49356eba2842..04646549bfdc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt @@ -15,12 +15,14 @@ */ package com.android.systemui.statusbar.phone.data.repository +import android.util.Log import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange import dagger.Binds import dagger.Module import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow /** Dark-mode state for tinting icons. */ @@ -33,8 +35,22 @@ class DarkIconRepositoryImpl @Inject constructor(private val darkIconDispatcherStore: SysuiDarkIconDispatcherStore) : DarkIconRepository { - override fun darkState(displayId: Int): StateFlow<DarkChange> = - darkIconDispatcherStore.forDisplay(displayId).darkChangeFlow() + override fun darkState(displayId: Int): StateFlow<DarkChange> { + val perDisplayDakIconDispatcher = darkIconDispatcherStore.forDisplay(displayId) + if (perDisplayDakIconDispatcher == null) { + Log.e( + TAG, + "DarkIconDispatcher for display $displayId is null. Returning flow of " + + "DarkChange.EMPTY", + ) + return MutableStateFlow(DarkChange.EMPTY) + } + return perDisplayDakIconDispatcher.darkChangeFlow() + } + + private companion object { + const val TAG = "DarkIconRepositoryImpl" + } } @Module diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt index ed8b3e8922f3..b15fffb8dd29 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt @@ -34,8 +34,8 @@ class LightsOutInteractor @Inject constructor(private val repository: StatusBarModeRepositoryStore) { - fun isLowProfile(displayId: Int): Flow<Boolean> = - repository.forDisplay(displayId).statusBarMode.map { + fun isLowProfile(displayId: Int): Flow<Boolean>? = + repository.forDisplay(displayId)?.statusBarMode?.map { when (it) { StatusBarMode.LIGHTS_OUT, StatusBarMode.LIGHTS_OUT_TRANSPARENT -> true diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index d257288637df..c31e34c50b06 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -47,6 +47,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; +import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlag; @@ -59,6 +60,9 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.core.StatusBarConnectedDisplays; import com.android.systemui.statusbar.core.StatusBarRootModernization; +import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; @@ -78,6 +82,8 @@ import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarVie import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; import com.android.systemui.util.CarrierConfigTracker; @@ -156,6 +162,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final NotificationIconContainerStatusBarViewBinder mNicViewBinder; private final DemoModeController mDemoModeController; + private final StatusBarWindowControllerStore mStatusBarWindowControllerStore; + private final StatusBarConfigurationControllerStore mStatusBarConfigurationControllerStore; + private final DarkIconDispatcherStore mDarkIconDispatcherStore; private List<String> mBlockedIcons = new ArrayList<>(); private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>(); @@ -263,7 +272,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue DumpManager dumpManager, StatusBarWindowStateController statusBarWindowStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, - DemoModeController demoModeController) { + DemoModeController demoModeController, + StatusBarWindowControllerStore statusBarWindowControllerStore, + StatusBarConfigurationControllerStore statusBarConfigurationControllerStore, + DarkIconDispatcherStore darkIconDispatcherStore) { mHomeStatusBarComponentFactory = homeStatusBarComponentFactory; mOngoingCallController = ongoingCallController; mAnimationScheduler = animationScheduler; @@ -287,6 +299,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mStatusBarWindowStateController = statusBarWindowStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mDemoModeController = demoModeController; + mStatusBarWindowControllerStore = statusBarWindowControllerStore; + mStatusBarConfigurationControllerStore = statusBarConfigurationControllerStore; + mDarkIconDispatcherStore = darkIconDispatcherStore; } private final DemoMode mDemoModeCallback = new DemoMode() { @@ -337,8 +352,27 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mDumpManager.registerDumpable(getDumpableName(), this); - mHomeStatusBarComponent = mHomeStatusBarComponentFactory.create( - (PhoneStatusBarView) getView()); + int displayId = view.getContext().getDisplayId(); + StatusBarConfigurationController configurationController = + mStatusBarConfigurationControllerStore.forDisplay(displayId); + if (configurationController == null) { + return; + } + StatusBarWindowController statusBarWindowController = + mStatusBarWindowControllerStore.forDisplay(displayId); + if (statusBarWindowController == null) { + return; + } + DarkIconDispatcher darkIconDispatcher = mDarkIconDispatcherStore.forDisplay(displayId); + if (darkIconDispatcher == null) { + return; + } + mHomeStatusBarComponent = + mHomeStatusBarComponentFactory.create( + (PhoneStatusBarView) getView(), + configurationController, + statusBarWindowController, + darkIconDispatcher); mHomeStatusBarComponent.init(); mStartableStates.clear(); for (Startable startable : mHomeStatusBarComponent.getStartables()) { @@ -453,6 +487,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onResume() { super.onResume(); + if (mHomeStatusBarComponent == null) { + return; + } mCommandQueue.addCallback(this); mStatusBarStateController.addCallback(this); initOngoingCallChip(); @@ -468,6 +505,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onPause() { super.onPause(); + if (mHomeStatusBarComponent == null) { + return; + } mCommandQueue.removeCallback(this); mStatusBarStateController.removeCallback(this); if (!StatusBarRootModernization.isEnabled()) { @@ -480,6 +520,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onDestroyView() { super.onDestroyView(); + if (mHomeStatusBarComponent == null) { + return; + } mStatusBarIconController.removeIconGroup(mDarkIconManager); mCarrierConfigTracker.removeCallback(mCarrierConfigCallback); mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java index f8ad0f2324bc..5837752abdaf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java @@ -20,6 +20,7 @@ import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.dagger.qualifiers.DisplaySpecific; import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.plugins.DarkIconDispatcher; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.LegacyLightsOutNotifController; @@ -29,6 +30,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; import com.android.systemui.statusbar.phone.StatusBarDemoMode; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; +import com.android.systemui.statusbar.window.StatusBarWindowController; import dagger.BindsInstance; import dagger.Subcomponent; @@ -57,7 +59,10 @@ public interface HomeStatusBarComponent { interface Factory { /** */ HomeStatusBarComponent create( - @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView); + @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView, + @BindsInstance StatusBarConfigurationController configurationController, + @BindsInstance StatusBarWindowController statusBarWindowController, + @BindsInstance @DisplaySpecific DarkIconDispatcher darkIconDispatcher); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java index 182f8d7e2fd6..6a331b938cf1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java @@ -22,19 +22,14 @@ import android.view.ViewStub; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.dagger.qualifiers.DisplaySpecific; import com.android.systemui.dagger.qualifiers.RootView; -import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.res.R; import com.android.systemui.statusbar.HeadsUpStatusBarView; -import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore; -import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; -import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.window.StatusBarWindowController; -import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import dagger.Module; import dagger.Provides; @@ -149,29 +144,4 @@ public interface HomeStatusBarModule { static int displayId(@RootView PhoneStatusBarView view) { return view.getContext().getDisplayId(); } - - /** */ - @Provides - @HomeStatusBarScope - static StatusBarConfigurationController configurationController( - @DisplaySpecific int displayId, StatusBarConfigurationControllerStore store) { - return store.forDisplay(displayId); - } - - /** */ - @Provides - @HomeStatusBarScope - static StatusBarWindowController provideWindowController( - @DisplaySpecific int displayId, StatusBarWindowControllerStore store) { - return store.forDisplay(displayId); - } - - /** */ - @Provides - @HomeStatusBarScope - @DisplaySpecific - static DarkIconDispatcher darkIconDispatcher( - @DisplaySpecific int displayId, DarkIconDispatcherStore store) { - return store.forDisplay(displayId); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt index c3299bbd40e6..7243ba7def58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt @@ -70,6 +70,8 @@ constructor( ) { fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView { val composeView = ComposeView(root.context) + val darkIconDispatcher = + darkIconDispatcherStore.forDisplay(root.context.displayId) ?: return composeView composeView.apply { setContent { StatusBarRoot( @@ -80,7 +82,7 @@ constructor( darkIconManagerFactory = darkIconManagerFactory, iconController = iconController, ongoingCallController = ongoingCallController, - darkIconDispatcher = darkIconDispatcherStore.forDisplay(root.context.displayId), + darkIconDispatcher = darkIconDispatcher, eventAnimationInteractor = eventAnimationInteractor, onViewCreated = andThen, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt index dcfbc5d6432d..c9cc17389c17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt @@ -63,6 +63,7 @@ import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -216,7 +217,7 @@ constructor( } else { combine( notificationsInteractor.areAnyNotificationsPresent, - lightsOutInteractor.isLowProfile(displayId), + lightsOutInteractor.isLowProfile(displayId) ?: flowOf(false), ) { hasNotifications, isLowProfile -> hasNotifications && isLowProfile } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java index 811a2ec44ccc..848e91d6f896 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java @@ -163,7 +163,18 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController mLp = getBarLayoutParams(mContext.getDisplay().getRotation()); Trace.endSection(); - mWindowManager.addView(mStatusBarWindowView, mLp); + try { + mWindowManager.addView(mStatusBarWindowView, mLp); + } catch (WindowManager.InvalidDisplayException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + Log.e( + TAG, + "Unable to add view to WindowManager. Display with id " + + mContext.getDisplayId() + + " doesn't exist anymore.", + e); + } mLpChanged.copyFrom(mLp); mContentInsetsProvider.addCallback(this::calculateStatusBarLocationsForAllRotations); @@ -176,7 +187,15 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController public void stop() { StatusBarConnectedDisplays.assertInNewMode(); - mWindowManager.removeView(mStatusBarWindowView); + try { + mWindowManager.removeView(mStatusBarWindowView); + } catch (IllegalArgumentException e) { + // Wrapping this in a try/catch to avoid crashes when a display is instantly removed + // after being added, and initialization hasn't finished yet. + // When that happens, adding the View to WindowManager fails, and therefore removing + // it here will fail too, since it wasn't added in the first place. + Log.e(TAG, "Failed to remove View from WindowManager. View was not attached", e); + } if (StatusBarRootModernization.isEnabled()) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt index 74031612f28e..f7688d2feab5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt @@ -54,19 +54,23 @@ constructor( StatusBarConnectedDisplays.assertInNewMode() } - override fun createInstanceForDisplay(displayId: Int): StatusBarWindowController { + override fun createInstanceForDisplay(displayId: Int): StatusBarWindowController? { val statusBarDisplayContext = displayWindowPropertiesRepository.get( displayId = displayId, windowType = WindowManager.LayoutParams.TYPE_STATUS_BAR, - ) + ) ?: return null + val statusBarConfigurationController = + statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null + val contentInsetsProvider = + statusBarContentInsetsProviderStore.forDisplay(displayId) ?: return null val viewCaptureAwareWindowManager = viewCaptureAwareWindowManagerFactory.create(statusBarDisplayContext.windowManager) return controllerFactory.create( statusBarDisplayContext.context, viewCaptureAwareWindowManager, - statusBarConfigurationControllerStore.forDisplay(displayId), - statusBarContentInsetsProviderStore.forDisplay(displayId), + statusBarConfigurationController, + contentInsetsProvider, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 411c81d13e0b..1fcf02d417e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -36,6 +36,8 @@ import static com.android.systemui.statusbar.phone.CentralSurfaces.MSG_DISMISS_K import static com.google.common.truth.Truth.assertThat; +import static kotlinx.coroutines.flow.FlowKt.flowOf; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -54,8 +56,6 @@ import static org.mockito.Mockito.when; import static java.util.Collections.emptySet; -import static kotlinx.coroutines.flow.FlowKt.flowOf; - import android.app.ActivityManager; import android.app.IWallpaperManager; import android.app.NotificationManager; @@ -132,6 +132,7 @@ import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.notetask.NoteTaskController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -175,6 +176,7 @@ import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.core.StatusBarConnectedDisplays; import com.android.systemui.statusbar.core.StatusBarInitializerImpl; import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationActivityStarter; @@ -216,6 +218,10 @@ import com.android.systemui.volume.VolumeComponent; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.StartingSurface; +import dagger.Lazy; + +import kotlinx.coroutines.test.TestScope; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -232,9 +238,6 @@ import java.util.Set; import javax.inject.Provider; -import dagger.Lazy; -import kotlinx.coroutines.test.TestScope; - @SmallTest @RunWith(AndroidJUnit4.class) @RunWithLooper(setAsMainLooper = true) @@ -536,6 +539,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { new StatusBarInitializerImpl( mStatusBarWindowController, mStatusBarModePerDisplayRepository, + mock(StatusBarConfigurationController.class), + mock(DarkIconDispatcher.class), mCollapsedStatusBarFragmentProvider, mock(StatusBarRootFactory.class), mock(HomeStatusBarComponent.Factory.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 0b4436755fa5..3a99328fa8ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -61,6 +62,9 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.core.StatusBarRootModernization; +import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder; @@ -75,6 +79,8 @@ import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatu import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewModel; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.StatusBarOperatorNameViewModel; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateListener; import com.android.systemui.util.CarrierConfigTracker; @@ -134,6 +140,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private StatusBarWindowStateController mStatusBarWindowStateController; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock private StatusBarWindowControllerStore mStatusBarWindowControllerStore; + @Mock private StatusBarWindowController mStatusBarWindowController; + @Mock private StatusBarConfigurationControllerStore mStatusBarConfigurationControllerStore; + @Mock private StatusBarConfigurationController mStatusBarConfigurationController; + @Mock private DarkIconDispatcherStore mDarkIconDispatcherStore; + @Mock private DarkIconDispatcher mDarkIconDispatcher; @Rule public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this); @@ -145,6 +157,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Before public void setup() { + when(mStatusBarWindowControllerStore.forDisplay(anyInt())) + .thenReturn(mStatusBarWindowController); + when(mStatusBarConfigurationControllerStore.forDisplay(anyInt())) + .thenReturn(mStatusBarConfigurationController); + when(mDarkIconDispatcherStore.forDisplay(anyInt())).thenReturn(mDarkIconDispatcher); + injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); mDependency.injectMockDependency(DarkIconDispatcher.class); @@ -1276,11 +1294,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mDumpManager, mStatusBarWindowStateController, mKeyguardUpdateMonitor, - mock(DemoModeController.class)); + mock(DemoModeController.class), + mStatusBarWindowControllerStore, + mStatusBarConfigurationControllerStore, + mDarkIconDispatcherStore); } private void setUpDaggerComponent() { - when(mStatusBarFragmentComponentFactory.create(any())) + when(mStatusBarFragmentComponentFactory.create(any(), any(), any(), any())) .thenReturn(mHomeStatusBarComponent); when(mHomeStatusBarComponent.getHeadsUpAppearanceController()) .thenReturn(mHeadsUpAppearanceController); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java index b8be6aa50015..64d89c5ac8ba 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java @@ -81,6 +81,14 @@ public class SysuiTestableContext extends TestableContext { return super.getDisplay(); } + @Override + public int getDisplayId() { + if (mCustomDisplay != null) { + return mCustomDisplay.getDisplayId(); + } + return super.getDisplayId(); + } + public SysuiTestableContext createDefaultDisplayContext() { Display display = getBaseContext().getSystemService(DisplayManager.class).getDisplays()[0]; return (SysuiTestableContext) createDisplayContext(display); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt index 50a19a9bc68a..fb2e2a3b76a9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.core +import com.android.systemui.plugins.DarkIconDispatcher +import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository import com.android.systemui.statusbar.window.StatusBarWindowController @@ -24,5 +26,7 @@ class FakeStatusBarInitializerFactory() : StatusBarInitializer.Factory { override fun create( statusBarWindowController: StatusBarWindowController, statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository, + statusBarConfigurationController: StatusBarConfigurationController, + darkIconDispatcher: DarkIconDispatcher, ): StatusBarInitializer = FakeStatusBarInitializer() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt index 6e990277df6b..b8dafb23b206 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt @@ -19,7 +19,9 @@ package com.android.systemui.statusbar.core import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.statusbar.data.repository.darkIconDispatcherStore import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository +import com.android.systemui.statusbar.data.repository.statusBarConfigurationControllerStore import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore val Kosmos.fakeStatusBarInitializer by Kosmos.Fixture { FakeStatusBarInitializer() } @@ -39,6 +41,8 @@ val Kosmos.multiDisplayStatusBarInitializerStore by fakeStatusBarInitializerFactory, fakeStatusBarWindowControllerStore, fakeStatusBarModeRepository, + statusBarConfigurationControllerStore, + darkIconDispatcherStore, ) } diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java index 56cb6f49f6c4..febf24edc294 100644 --- a/services/core/java/com/android/server/input/InputSettingsObserver.java +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -71,6 +71,9 @@ class InputSettingsObserver extends ContentObserver { (reason) -> updateMouseSwapPrimaryButton()), Map.entry(Settings.System.getUriFor(Settings.System.MOUSE_SCROLLING_ACCELERATION), (reason) -> updateMouseScrollingAcceleration()), + Map.entry(Settings.System.getUriFor( + Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED), + (reason) -> updateMouseAccelerationEnabled()), Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_POINTER_SPEED), (reason) -> updateTouchpadPointerSpeed()), Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING), @@ -191,6 +194,11 @@ class InputSettingsObserver extends ContentObserver { InputSettings.isMouseScrollingAccelerationEnabled(mContext)); } + private void updateMouseAccelerationEnabled() { + mNative.setMouseAccelerationEnabled( + InputSettings.isMousePointerAccelerationEnabled(mContext)); + } + private void updateTouchpadPointerSpeed() { mNative.setTouchpadPointerSpeed( constrainPointerSpeedValue(InputSettings.getTouchpadPointerSpeed(mContext))); diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index ab5a680867e9..7dbde64a6412 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -138,6 +138,8 @@ interface NativeInputManagerService { void setMouseSwapPrimaryButtonEnabled(boolean enabled); + void setMouseAccelerationEnabled(boolean enabled); + void setTouchpadPointerSpeed(int speed); void setTouchpadNaturalScrollingEnabled(boolean enabled); @@ -429,6 +431,9 @@ interface NativeInputManagerService { public native void setMouseSwapPrimaryButtonEnabled(boolean enabled); @Override + public native void setMouseAccelerationEnabled(boolean enabled); + + @Override public native void setTouchpadPointerSpeed(int speed); @Override diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index eeae6166873a..f50e8aa7eb7b 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3082,16 +3082,42 @@ public class NotificationManagerService extends SystemService { private void sendRegisteredOnlyBroadcast(Intent baseIntent) { int[] userIds = mUmInternal.getProfileIds(mAmi.getCurrentUserId(), true); - Intent intent = new Intent(baseIntent).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - for (int userId : userIds) { - getContext().sendBroadcastAsUser(intent, UserHandle.of(userId), null); - } - // explicitly send the broadcast to all DND packages, even if they aren't currently running - for (int userId : userIds) { - for (String pkg : mConditionProviders.getAllowedPackages(userId)) { - Intent pkgIntent = new Intent(baseIntent).setPackage(pkg).setFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - getContext().sendBroadcastAsUser(pkgIntent, UserHandle.of(userId)); + if (Flags.nmBinderPerfReduceZenBroadcasts()) { + for (int userId : userIds) { + Context userContext = getContext().createContextAsUser(UserHandle.of(userId), 0); + String[] dndPackages = mConditionProviders.getAllowedPackages(userId) + .toArray(new String[0]); + + // We send the broadcast to all DND packages in the second step, so leave them out + // of this first broadcast for *running* receivers. That ensures each package only + // receives it once. + Intent registeredOnlyIntent = new Intent(baseIntent) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + userContext.sendBroadcastMultiplePermissions(registeredOnlyIntent, + /* receiverPermissions= */ new String[0], + /* excludedPermissions= */ new String[0], + /* excludedPackages= */ dndPackages); + + for (String pkg : dndPackages) { + Intent pkgIntent = new Intent(baseIntent).setPackage(pkg) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + userContext.sendBroadcast(pkgIntent); + } + } + } else { + Intent intent = new Intent(baseIntent).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + for (int userId : userIds) { + getContext().sendBroadcastAsUser(intent, UserHandle.of(userId), null); + } + + // explicitly send the broadcast to all DND packages, even if they aren't currently + // running + for (int userId : userIds) { + for (String pkg : mConditionProviders.getAllowedPackages(userId)) { + Intent pkgIntent = new Intent(baseIntent).setPackage(pkg).setFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + getContext().sendBroadcastAsUser(pkgIntent, UserHandle.of(userId)); + } } } } diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig index 65a38ae1fcde..f15c23e110a4 100644 --- a/services/core/java/com/android/server/notification/flags.aconfig +++ b/services/core/java/com/android/server/notification/flags.aconfig @@ -187,3 +187,13 @@ flag { description: "Enables sound uri with vibration source in notification channel" bug: "351975435" } + +flag { + name: "nm_binder_perf_reduce_zen_broadcasts" + namespace: "systemui" + description: "Don't send duplicate zen-related (policy changed, etc) broadcasts" + bug: "324376849" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index cba606cf2b0c..98ed6f76b2f9 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -101,7 +101,8 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { // isLeashReadyForDispatching (used to dispatch the leash of the control) is // depending on mGivenInsetsReady. Therefore, triggering notifyControlChanged here // again, so that the control with leash can be eventually dispatched - if (!mGivenInsetsReady && isServerVisible() && !givenInsetsPending) { + if (!mGivenInsetsReady && isServerVisible() && !givenInsetsPending + && mControlTarget != null) { mGivenInsetsReady = true; ImeTracker.forLogging().onProgress(mStatsToken, ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED); diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 7276007481ab..d1585d06ae40 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -384,7 +384,7 @@ class InsetsSourceProvider { } final boolean serverVisibleChanged = mServerVisible != isServerVisible; setServerVisible(isServerVisible); - if (mControl != null) { + if (mControl != null && mControlTarget != null) { final boolean positionChanged = updateInsetsControlPosition(windowState); if (!(positionChanged || mHasPendingPosition) // The insets hint would be updated while changing the position. Here updates it diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index ce8518449230..9df65f60e8d7 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -371,7 +371,7 @@ class InsetsStateController { array.add(provider); } - void notifyControlChanged(InsetsControlTarget target, InsetsSourceProvider provider) { + void notifyControlChanged(@NonNull InsetsControlTarget target, InsetsSourceProvider provider) { addToPendingControlMaps(target, provider); notifyPendingInsetsControlChanged(); } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 813fec1454a7..f634beb77329 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -345,6 +345,7 @@ public: void setMouseReverseVerticalScrollingEnabled(bool enabled); void setMouseScrollingAccelerationEnabled(bool enabled); void setMouseSwapPrimaryButtonEnabled(bool enabled); + void setMouseAccelerationEnabled(bool enabled); void setTouchpadPointerSpeed(int32_t speed); void setTouchpadNaturalScrollingEnabled(bool enabled); void setTouchpadTapToClickEnabled(bool enabled); @@ -502,6 +503,9 @@ private: // True if the mouse primary button is swapped (left/right buttons). bool mouseSwapPrimaryButtonEnabled{false}; + // True if the mouse cursor will accelerate as the mouse moves faster. + bool mousePointerAccelerationEnabled{true}; + // The touchpad pointer speed, as a number from -7 (slowest) to 7 (fastest). int32_t touchpadPointerSpeed{0}; @@ -847,6 +851,7 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon outConfig->mouseReverseVerticalScrollingEnabled = mLocked.mouseReverseVerticalScrollingEnabled; outConfig->mouseSwapPrimaryButtonEnabled = mLocked.mouseSwapPrimaryButtonEnabled; + outConfig->mousePointerAccelerationEnabled = mLocked.mousePointerAccelerationEnabled; outConfig->touchpadPointerSpeed = mLocked.touchpadPointerSpeed; outConfig->touchpadNaturalScrollingEnabled = mLocked.touchpadNaturalScrollingEnabled; @@ -1458,6 +1463,21 @@ void NativeInputManager::setMouseSwapPrimaryButtonEnabled(bool enabled) { InputReaderConfiguration::Change::MOUSE_SETTINGS); } +void NativeInputManager::setMouseAccelerationEnabled(bool enabled) { + { // acquire lock + std::scoped_lock _l(mLock); + + if (mLocked.mousePointerAccelerationEnabled == enabled) { + return; + } + + mLocked.mousePointerAccelerationEnabled = enabled; + } // release lock + + mInputManager->getReader().requestRefreshConfiguration( + InputReaderConfiguration::Change::POINTER_SPEED); +} + void NativeInputManager::setPointerSpeed(int32_t speed) { { // acquire lock std::scoped_lock _l(mLock); @@ -3220,6 +3240,11 @@ static void nativeSetMouseSwapPrimaryButtonEnabled(JNIEnv* env, jobject nativeIm im->setMouseSwapPrimaryButtonEnabled(enabled); } +static void nativeSetMouseAccelerationEnabled(JNIEnv* env, jobject nativeImplObj, bool enabled) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); + im->setMouseAccelerationEnabled(enabled); +} + static jboolean nativeSetKernelWakeEnabled(JNIEnv* env, jobject nativeImplObj, jint deviceId, jboolean enabled) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); @@ -3280,6 +3305,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"setMouseScrollingAccelerationEnabled", "(Z)V", (void*)nativeSetMouseScrollingAccelerationEnabled}, {"setMouseSwapPrimaryButtonEnabled", "(Z)V", (void*)nativeSetMouseSwapPrimaryButtonEnabled}, + {"setMouseAccelerationEnabled", "(Z)V", (void*)nativeSetMouseAccelerationEnabled}, {"setTouchpadPointerSpeed", "(I)V", (void*)nativeSetTouchpadPointerSpeed}, {"setTouchpadNaturalScrollingEnabled", "(Z)V", (void*)nativeSetTouchpadNaturalScrollingEnabled}, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index bcda2c0662ca..301165f8151d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -644,6 +644,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { doNothing().when(mContext).sendBroadcast(any(), anyString()); doNothing().when(mContext).sendBroadcastAsUser(any(), any()); doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); + doNothing().when(mContext).sendBroadcastMultiplePermissions(any(), any(), any(), any()); + doReturn(mContext).when(mContext).createContextAsUser(eq(mUser), anyInt()); + TestableContentResolver cr = mock(TestableContentResolver.class); when(mContext.getContentResolver()).thenReturn(cr); doNothing().when(cr).registerContentObserver(any(), anyBoolean(), any(), anyInt()); @@ -11235,7 +11238,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void onZenModeChanged_sendsBroadcasts() throws Exception { + @DisableFlags(Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS) + public void onZenModeChanged_sendsBroadcasts_oldBehavior() throws Exception { when(mAmi.getCurrentUserId()).thenReturn(100); when(mUmInternal.getProfileIds(eq(100), anyBoolean())).thenReturn(new int[]{100, 101, 102}); when(mConditionProviders.getAllowedPackages(anyInt())).then(new Answer<List<String>>() { @@ -11288,6 +11292,74 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + @EnableFlags(Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS) + public void onZenModeChanged_sendsBroadcasts() throws Exception { + when(mAmi.getCurrentUserId()).thenReturn(100); + when(mUmInternal.getProfileIds(eq(100), anyBoolean())).thenReturn(new int[]{100, 101, 102}); + when(mConditionProviders.getAllowedPackages(anyInt())).then(new Answer<List<String>>() { + @Override + public List<String> answer(InvocationOnMock invocation) { + int userId = invocation.getArgument(0); + switch (userId) { + case 100: + return Lists.newArrayList("a", "b", "c"); + case 101: + return Lists.newArrayList(); + case 102: + return Lists.newArrayList("b"); + default: + throw new IllegalArgumentException( + "Why would you ask for packages of userId " + userId + "?"); + } + } + }); + Context context100 = mock(Context.class); + doReturn(context100).when(mContext).createContextAsUser(eq(UserHandle.of(100)), anyInt()); + Context context101 = mock(Context.class); + doReturn(context101).when(mContext).createContextAsUser(eq(UserHandle.of(101)), anyInt()); + Context context102 = mock(Context.class); + doReturn(context102).when(mContext).createContextAsUser(eq(UserHandle.of(102)), anyInt()); + + mService.getBinderService().setZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS, null, + "testing!", false); + waitForIdle(); + + // Verify broadcasts per user: registered receivers first, then DND packages. + InOrder inOrder = inOrder(context100, context101, context102); + + inOrder.verify(context100).sendBroadcastMultiplePermissions( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)), + eq(new String[0]), eq(new String[0]), eq(new String[] {"a", "b", "c"})); + inOrder.verify(context100).sendBroadcast( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setPackage("a") + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT))); + inOrder.verify(context100).sendBroadcast( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setPackage("b") + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT))); + inOrder.verify(context100).sendBroadcast( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setPackage("c") + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT))); + + inOrder.verify(context101).sendBroadcastMultiplePermissions( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)), + eq(new String[0]), eq(new String[0]), eq(new String[] {})); + + inOrder.verify(context102).sendBroadcastMultiplePermissions( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)), + eq(new String[0]), eq(new String[0]), eq(new String[] {"b"})); + inOrder.verify(context102).sendBroadcast( + eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED) + .setPackage("b") + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT))); + } + + @Test @EnableFlags(android.app.Flags.FLAG_MODES_API) public void onAutomaticRuleStatusChanged_sendsBroadcastToRuleOwner() throws Exception { mService.mZenModeHelper.getCallbacks().forEach(c -> c.onAutomaticRuleStatusChanged( diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index e7c9e927b311..e27dbe588183 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1869,10 +1869,18 @@ public class UsageStatsService extends SystemService implements } private boolean shouldDeleteObsoleteData(UserHandle userHandle) { - final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); - // If a profile owner is not defined for the given user, obsolete data should be deleted - return dpmInternal == null - || dpmInternal.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle) == null; + if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) { + final SupervisionManagerInternal smInternal = getSupervisionManagerInternal(); + // If supervision is not enabled for the given user, obsolete data should be deleted. + return smInternal == null + || !smInternal.isSupervisionEnabledForUser(userHandle.getIdentifier()); + } else { + final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); + // If a profile owner is not defined for the given user, obsolete data should be deleted + return dpmInternal == null + || dpmInternal.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle) + == null; + } } private String buildFullToken(String packageName, String token) { |