diff options
22 files changed, 345 insertions, 194 deletions
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig index ba1473cf5ed7..67ade79e1b94 100644 --- a/core/java/android/companion/virtual/flags/flags.aconfig +++ b/core/java/android/companion/virtual/flags/flags.aconfig @@ -135,3 +135,11 @@ flag { description: "Show virtual devices in Settings" bug: "338974320" } + +flag { + name: "migrate_viewconfiguration_constants_to_resources" + namespace: "virtual_devices" + description: "Use resources instead of constants in ViewConfiguration" + is_fixed_read_only: true + bug: "370928384" +} diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index d94450b1cabd..a029f56cf1d7 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -26,14 +26,14 @@ flag { name: "enable_le_audio_sharing" namespace: "pixel_cross_device_control" description: "Gates whether to enable LE audio sharing" - bug: "323125723" + bug: "388674074" } flag { name: "enable_le_audio_qr_code_private_broadcast_sharing" namespace: "pixel_cross_device_control" description: "Gates whether to enable LE audio private broadcast sharing via QR code" - bug: "323125723" + bug: "388674074" } flag { @@ -229,3 +229,23 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "adopt_primary_group_management_api_v2" + namespace: "cross_device_experiences" + description: "Adopt more Bluetooth LE broadcast primary group management APIs post launch" + bug: "381946931" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "promote_audio_sharing_for_second_auto_connected_lea_device" + namespace: "cross_device_experiences" + description: "Show audio sharing promote notification or dialog when the second lea device is auto connected" + bug: "395786392" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java index 572444edea29..bf86911ee683 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java @@ -30,13 +30,11 @@ import android.util.Log; import androidx.annotation.ChecksSdkIntAtLeast; import com.android.internal.annotations.VisibleForTesting; -import com.android.settingslib.flags.Flags; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -387,7 +385,7 @@ public class CsipDeviceManager { preferredMainDevice.refresh(); hasChanged = true; } - syncAudioSharingStatusIfNeeded(preferredMainDevice); + syncAudioSharingSourceIfNeeded(preferredMainDevice); } if (hasChanged) { log("addMemberDevicesIntoMainDevice: After changed, CachedBluetoothDevice list: " @@ -401,16 +399,13 @@ public class CsipDeviceManager { return userManager != null && userManager.isManagedProfile(); } - private void syncAudioSharingStatusIfNeeded(CachedBluetoothDevice mainDevice) { + private void syncAudioSharingSourceIfNeeded(CachedBluetoothDevice mainDevice) { boolean isAudioSharingEnabled = BluetoothUtils.isAudioSharingUIAvailable(mContext); - if (isAudioSharingEnabled && mainDevice != null) { + if (isAudioSharingEnabled) { if (isWorkProfile()) { - log("addMemberDevicesIntoMainDevice: skip sync audio sharing status, work profile"); + log("addMemberDevicesIntoMainDevice: skip sync source for work profile"); return; } - Set<CachedBluetoothDevice> deviceSet = new HashSet<>(); - deviceSet.add(mainDevice); - deviceSet.addAll(mainDevice.getMemberDevice()); boolean hasBroadcastSource = BluetoothUtils.isBroadcasting(mBtManager) && BluetoothUtils.hasConnectedBroadcastSource( mainDevice, mBtManager); @@ -424,6 +419,9 @@ public class CsipDeviceManager { if (metadata != null && assistant != null) { log("addMemberDevicesIntoMainDevice: sync audio sharing source after " + "combining the top level devices."); + Set<CachedBluetoothDevice> deviceSet = new HashSet<>(); + deviceSet.add(mainDevice); + deviceSet.addAll(mainDevice.getMemberDevice()); Set<BluetoothDevice> sinksToSync = deviceSet.stream() .map(CachedBluetoothDevice::getDevice) .filter(device -> @@ -437,24 +435,8 @@ public class CsipDeviceManager { } } } - if (Flags.enableTemporaryBondDevicesUi()) { - log("addMemberDevicesIntoMainDevice: sync temp bond metadata for audio sharing " - + "sinks after combining the top level devices."); - Set<BluetoothDevice> sinksToSync = deviceSet.stream() - .map(CachedBluetoothDevice::getDevice).filter(Objects::nonNull).collect( - Collectors.toSet()); - if (sinksToSync.stream().anyMatch(BluetoothUtils::isTemporaryBondDevice)) { - for (BluetoothDevice device : sinksToSync) { - if (!BluetoothUtils.isTemporaryBondDevice(device)) { - log("addMemberDevicesIntoMainDevice: sync temp bond metadata for " - + device.getAnonymizedAddress()); - BluetoothUtils.setTemporaryBondMetadata(device); - } - } - } - } } else { - log("addMemberDevicesIntoMainDevice: skip sync audio sharing status, flag disabled"); + log("addMemberDevicesIntoMainDevice: skip sync source, flag disabled"); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java index 2eccaa626f3b..fd14d1ff6786 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java @@ -40,8 +40,6 @@ import android.content.Context; import android.os.Looper; import android.os.Parcel; import android.os.UserManager; -import android.platform.test.annotations.DisableFlags; -import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import com.android.settingslib.flags.Flags; @@ -76,9 +74,6 @@ public class CsipDeviceManagerTest { private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11"; private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22"; private final static String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33"; - private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25; - private static final String TEMP_BOND_METADATA = - "<TEMP_BOND_TYPE>le_audio_sharing</TEMP_BOND_TYPE>"; private final static int GROUP1 = 1; private final BluetoothClass DEVICE_CLASS_1 = createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES); @@ -342,7 +337,6 @@ public class CsipDeviceManagerTest { } @Test - @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING) public void addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_returnTrue() { // Condition: The preferredDevice is main and there is another main device in top list // Expected Result: return true and there is the preferredDevice in top list @@ -352,6 +346,7 @@ public class CsipDeviceManagerTest { mCachedDevices.add(preferredDevice); mCachedDevices.add(mCachedDevice2); mCachedDevices.add(mCachedDevice3); + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice)) .isTrue(); @@ -364,7 +359,6 @@ public class CsipDeviceManagerTest { } @Test - @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) public void addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_workProfile_doNothing() { // Condition: The preferredDevice is main and there is another main device in top list @@ -375,6 +369,7 @@ public class CsipDeviceManagerTest { mCachedDevices.add(preferredDevice); mCachedDevices.add(mCachedDevice2); mCachedDevices.add(mCachedDevice3); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); when(mBroadcast.isEnabled(null)).thenReturn(true); BluetoothLeBroadcastMetadata metadata = Mockito.mock(BluetoothLeBroadcastMetadata.class); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(metadata); @@ -382,8 +377,6 @@ public class CsipDeviceManagerTest { BluetoothLeBroadcastReceiveState.class); when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L)); when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state)); - when(mDevice2.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)) - .thenReturn(TEMP_BOND_METADATA.getBytes()); when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); when(mUserManager.isManagedProfile()).thenReturn(true); @@ -394,13 +387,10 @@ public class CsipDeviceManagerTest { assertThat(mCachedDevices.contains(mCachedDevice3)).isTrue(); assertThat(preferredDevice.getMemberDevice()).contains(mCachedDevice2); verify(mAssistant, never()).addSource(mDevice1, metadata, /* isGroupOp= */ false); - verify(mDevice1, never()).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); } @Test - @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) - public void addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_syncState() { + public void addMemberDevicesIntoMainDevice_preferredDeviceIsMainAndTwoMain_syncSource() { // Condition: The preferredDevice is main and there is another main device in top list // Expected Result: return true and there is the preferredDevice in top list CachedBluetoothDevice preferredDevice = mCachedDevice1; @@ -409,6 +399,7 @@ public class CsipDeviceManagerTest { mCachedDevices.add(preferredDevice); mCachedDevices.add(mCachedDevice2); mCachedDevices.add(mCachedDevice3); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); when(mBroadcast.isEnabled(null)).thenReturn(true); BluetoothLeBroadcastMetadata metadata = Mockito.mock(BluetoothLeBroadcastMetadata.class); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(metadata); @@ -416,8 +407,6 @@ public class CsipDeviceManagerTest { BluetoothLeBroadcastReceiveState.class); when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L)); when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state)); - when(mDevice2.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)) - .thenReturn(TEMP_BOND_METADATA.getBytes()); assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice)) .isTrue(); @@ -426,8 +415,6 @@ public class CsipDeviceManagerTest { assertThat(mCachedDevices.contains(mCachedDevice3)).isTrue(); assertThat(preferredDevice.getMemberDevice()).contains(mCachedDevice2); verify(mAssistant).addSource(mDevice1, metadata, /* isGroupOp= */ false); - verify(mDevice1).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); } @Test @@ -449,13 +436,13 @@ public class CsipDeviceManagerTest { } @Test - @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) public void addMemberDevicesIntoMainDevice_preferredDeviceIsMemberAndTwoMain_returnTrue() { // Condition: The preferredDevice is member and there are two main device in top list // Expected Result: return true and there is the preferredDevice in top list CachedBluetoothDevice preferredDevice = mCachedDevice2; BluetoothDevice expectedMainBluetoothDevice = preferredDevice.getDevice(); mCachedDevice3.setGroupId(GROUP1); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); when(mBroadcast.isEnabled(null)).thenReturn(false); assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice)) @@ -470,20 +457,16 @@ public class CsipDeviceManagerTest { assertThat(mCachedDevice1.getDevice()).isEqualTo(expectedMainBluetoothDevice); verify(mAssistant, never()).addSource(any(BluetoothDevice.class), any(BluetoothLeBroadcastMetadata.class), anyBoolean()); - verify(mDevice2, never()).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); - verify(mDevice3, never()).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); } @Test - @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI}) - public void addMemberDevicesIntoMainDevice_preferredDeviceIsMemberAndTwoMain_syncState() { + public void addMemberDevicesIntoMainDevice_preferredDeviceIsMemberAndTwoMain_syncSource() { // Condition: The preferredDevice is member and there are two main device in top list // Expected Result: return true and there is the preferredDevice in top list CachedBluetoothDevice preferredDevice = mCachedDevice2; BluetoothDevice expectedMainBluetoothDevice = preferredDevice.getDevice(); mCachedDevice3.setGroupId(GROUP1); + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); when(mBroadcast.isEnabled(null)).thenReturn(true); BluetoothLeBroadcastMetadata metadata = Mockito.mock(BluetoothLeBroadcastMetadata.class); when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(metadata); @@ -491,8 +474,6 @@ public class CsipDeviceManagerTest { BluetoothLeBroadcastReceiveState.class); when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L)); when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of(state)); - when(mDevice1.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)) - .thenReturn(TEMP_BOND_METADATA.getBytes()); assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice)) .isTrue(); @@ -507,10 +488,6 @@ public class CsipDeviceManagerTest { assertThat(mCachedDevice1.getDevice()).isEqualTo(expectedMainBluetoothDevice); verify(mAssistant).addSource(mDevice2, metadata, /* isGroupOp= */ false); verify(mAssistant).addSource(mDevice3, metadata, /* isGroupOp= */ false); - verify(mDevice2).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); - verify(mDevice3).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, - TEMP_BOND_METADATA.getBytes()); } @Test diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt index 60eaa28e3822..b9aca25e1675 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt @@ -21,7 +21,6 @@ import com.android.compose.animation.scene.MutableSceneTransitionLayoutState import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey -import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.observableTransitionState import com.android.systemui.scene.shared.model.SceneDataSource import kotlinx.coroutines.CoroutineScope @@ -106,6 +105,6 @@ class SceneTransitionLayoutDataSource( } override fun freezeAndAnimateToCurrentState() { - (state.transitionState as? TransitionState.Transition)?.freezeAndAnimateToCurrentState() + state.currentTransition?.freezeAndAnimateToCurrentState() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java index fee358a7c15d..83860ecf168b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java @@ -63,6 +63,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.List; +import java.util.Optional; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; @@ -100,7 +101,7 @@ public class RotationLockTileTest extends SysuiTestCase { @Mock private BatteryController mBatteryController; @Mock - DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController; + Optional<DeviceStateRotationLockSettingController> mDeviceStateRotationLockSettingController; @Mock RotationPolicyWrapper mRotationPolicyWrapper; @Mock diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java index 8593f6a08b5a..605e4a47275b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java @@ -37,15 +37,19 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Optional; + @RunWith(AndroidJUnit4.class) @TestableLooper.RunWithLooper @SmallTest public class RotationLockControllerImplTest extends SysuiTestCase { - private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"}; + private static final String[] DEFAULT_SETTINGS = new String[]{"0:0", "1:2"}; - @Mock RotationPolicyWrapper mRotationPolicyWrapper; - @Mock DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController; + @Mock + RotationPolicyWrapper mRotationPolicyWrapper; + @Mock + DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController; private ArgumentCaptor<RotationPolicy.RotationPolicyListener> mRotationPolicyListenerCaptor; @@ -93,7 +97,7 @@ public class RotationLockControllerImplTest extends SysuiTestCase { private void createRotationLockController(String[] deviceStateRotationLockDefaults) { new RotationLockControllerImpl( mRotationPolicyWrapper, - mDeviceStateRotationLockSettingController, + Optional.of(mDeviceStateRotationLockSettingController), deviceStateRotationLockDefaults); } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 3c68e3a09f02..a25faa3a7aec 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -56,6 +56,7 @@ import com.android.systemui.reardisplay.RearDisplayModule; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; import com.android.systemui.recents.RecentsModule; +import com.android.systemui.rotationlock.DeviceStateAutoRotateModule; import com.android.systemui.rotationlock.RotationLockModule; import com.android.systemui.rotationlock.RotationLockNewModule; import com.android.systemui.scene.SceneContainerFrameworkModule; @@ -132,6 +133,7 @@ import javax.inject.Named; CollapsedStatusBarFragmentStartableModule.class, ConnectingDisplayViewModel.StartableModule.class, DefaultBlueprintModule.class, + DeviceStateAutoRotateModule.class, EmergencyGestureModule.class, GestureModule.class, HeadsUpModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index fe5a82cb5b8c..f8cf6b007041 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -105,6 +105,7 @@ import com.android.systemui.qs.footer.dagger.FooterActionsModule; import com.android.systemui.recents.Recents; import com.android.systemui.recordissue.RecordIssueModule; import com.android.systemui.retail.RetailModeModule; +import com.android.systemui.rotationlock.DeviceStateAutoRotateModule.BoundsDeviceStateAutoRotateModule; import com.android.systemui.scene.shared.model.SceneContainerConfig; import com.android.systemui.scene.shared.model.SceneDataSource; import com.android.systemui.scene.shared.model.SceneDataSourceDelegator; @@ -145,6 +146,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.ConfigurationControllerModule; import com.android.systemui.statusbar.phone.LetterboxModule; import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule; +import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.PolicyModule; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; @@ -391,6 +393,11 @@ public abstract class SystemUIModule { @BindsOptionalOf abstract LockscreenContent optionalLockscreenContent(); + @BindsOptionalOf + @BoundsDeviceStateAutoRotateModule + abstract Optional<DeviceStateRotationLockSettingController> + optionalDeviceStateRotationLockSettingController(); + @SysUISingleton @Binds abstract SystemClock bindSystemClock(SystemClockImpl systemClock); @@ -466,6 +473,16 @@ public abstract class SystemUIModule { return new SceneDataSourceDelegator(applicationScope, config); } + @Provides + @SysUISingleton + static Optional<DeviceStateRotationLockSettingController> + provideDeviceStateRotationLockSettingController( + @BoundsDeviceStateAutoRotateModule + Optional<Optional<DeviceStateRotationLockSettingController>> optionalOfOptional + ) { + return optionalOfOptional.orElseGet(Optional::empty); + } + @Binds abstract SceneDataSource bindSceneDataSource(SceneDataSourceDelegator delegator); diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/DeviceStateAutoRotateModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/DeviceStateAutoRotateModule.kt new file mode 100644 index 000000000000..628280210236 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/rotationlock/DeviceStateAutoRotateModule.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.rotationlock + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingController +import com.android.window.flags.Flags +import dagger.Module +import dagger.Provides +import java.util.Optional +import javax.inject.Provider +import javax.inject.Qualifier + +@Module +class DeviceStateAutoRotateModule { + /** Qualifier for dependencies to be bound with [DeviceStateAutoRotateModule]. */ + @Qualifier + @MustBeDocumented + @Retention(AnnotationRetention.RUNTIME) + annotation class BoundsDeviceStateAutoRotateModule + + /** + * Provides an instance of [DeviceStateRotationLockSettingController]. + * + * @param controllerProvider The provider for [DeviceStateRotationLockSettingController]. + * @return An [Optional] containing the [DeviceStateRotationLockSettingController] instance if + * the `Flags.enableDeviceStateAutoRotateSettingRefactor()` flag is disabled, or an empty + * [Optional] otherwise. + */ + @Provides + @BoundsDeviceStateAutoRotateModule + @SysUISingleton + fun provideDeviceStateRotationLockSettingController( + controllerProvider: Provider<DeviceStateRotationLockSettingController> + ): Optional<DeviceStateRotationLockSettingController> = + if (Flags.enableDeviceStateAutoRotateSettingRefactor()) { + Optional.empty() + } else { + Optional.of(controllerProvider.get()) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 6c8e1825ea0a..ba41fd4c40ab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -65,6 +65,7 @@ import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeDisplayAware; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor; +import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationClickNotifier; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -116,6 +117,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final static String TAG = "StatusBarNotificationActivityStarter"; private final Context mContext; + private final ShadeDialogContextInteractor mContextInteractor; private final Handler mMainThreadHandler; private final Executor mUiBgExecutor; @@ -156,6 +158,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @Inject StatusBarNotificationActivityStarter( @ShadeDisplayAware Context context, + ShadeDialogContextInteractor contextInteractor, @Main Handler mainThreadHandler, @Background Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, @@ -188,6 +191,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit PowerInteractor powerInteractor, UserTracker userTracker) { mContext = context; + mContextInteractor = contextInteractor; mMainThreadHandler = mainThreadHandler; mUiBgExecutor = uiBgExecutor; mVisibilityProvider = visibilityProvider; @@ -491,7 +495,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit boolean animate, boolean isActivityIntent) { mLogger.logStartNotificationIntent(entry); - final int displayId = mContext.getDisplayId(); + final int displayId = mContextInteractor.getContext().getDisplayId(); try { ActivityTransitionAnimator.Controller animationController = new StatusBarTransitionAnimatorController( @@ -532,7 +536,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit public void startNotificationGutsIntent(@NonNull final Intent intent, final int appUid, @NonNull ExpandableNotificationRow row) { boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); - final int displayId = mContext.getDisplayId(); + final int displayId = mContextInteractor.getContext().getDisplayId(); ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { @Override public boolean onDismiss() { @@ -571,7 +575,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @Override public void startHistoryIntent(View view, boolean showHistory) { ModesEmptyShadeFix.assertInLegacyMode(); - final int displayId = mContext.getDisplayId(); + final int displayId = mContextInteractor.getContext().getDisplayId(); boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { @Override @@ -621,7 +625,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @Override public void startSettingsIntent(@NonNull View view, @NonNull SettingsIntent intentInfo) { - final int displayId = mContext.getDisplayId(); + final int displayId = mContextInteractor.getContext().getDisplayId(); boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java index 88cf46a0ca07..b13e01be40f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java @@ -29,7 +29,6 @@ import androidx.annotation.NonNull; import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import com.android.systemui.Dumpable; -import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.util.wrapper.RotationPolicyWrapper; @@ -43,7 +42,6 @@ import javax.inject.Inject; * Handles reading and writing of rotation lock settings per device state, as well as setting the * rotation lock when device state changes. */ -@SysUISingleton public final class DeviceStateRotationLockSettingController implements Listenable, RotationLockController.RotationLockControllerCallback, Dumpable { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java index 797aa1f3a3dd..3ee7f33e3d3a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java @@ -27,8 +27,10 @@ import androidx.annotation.NonNull; import com.android.internal.view.RotationPolicy.RotationPolicyListener; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.rotationlock.DeviceStateAutoRotateModule.BoundsDeviceStateAutoRotateModule; import com.android.systemui.util.wrapper.RotationPolicyWrapper; +import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import javax.inject.Inject; @@ -50,21 +52,25 @@ public final class RotationLockControllerImpl implements RotationLockController }; private final RotationPolicyWrapper mRotationPolicy; - private final DeviceStateRotationLockSettingController + private final Optional<DeviceStateRotationLockSettingController> mDeviceStateRotationLockSettingController; private final boolean mIsPerDeviceStateRotationLockEnabled; @Inject public RotationLockControllerImpl( RotationPolicyWrapper rotationPolicyWrapper, - DeviceStateRotationLockSettingController deviceStateRotationLockSettingController, + Optional<DeviceStateRotationLockSettingController> + deviceStateRotationLockSettingController, @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults ) { mRotationPolicy = rotationPolicyWrapper; - mDeviceStateRotationLockSettingController = deviceStateRotationLockSettingController; mIsPerDeviceStateRotationLockEnabled = deviceStateRotationLockDefaults.length > 0; - if (mIsPerDeviceStateRotationLockEnabled) { - mCallbacks.add(mDeviceStateRotationLockSettingController); + mDeviceStateRotationLockSettingController = + deviceStateRotationLockSettingController; + + if (mIsPerDeviceStateRotationLockEnabled + && mDeviceStateRotationLockSettingController.isPresent()) { + mCallbacks.add(mDeviceStateRotationLockSettingController.get()); } setListening(true); @@ -113,8 +119,9 @@ public final class RotationLockControllerImpl implements RotationLockController } else { mRotationPolicy.unregisterRotationPolicyListener(mRotationPolicyListener); } - if (mIsPerDeviceStateRotationLockEnabled) { - mDeviceStateRotationLockSettingController.setListening(listening); + if (mIsPerDeviceStateRotationLockEnabled + && mDeviceStateRotationLockSettingController.isPresent()) { + mDeviceStateRotationLockSettingController.get().setListening(listening); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 3190d3ae8f16..28eafa937097 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -76,6 +76,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeControllerImpl; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.shade.data.repository.ShadeAnimationRepository; +import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractorLegacyImpl; import com.android.systemui.statusbar.CommandQueue; @@ -171,6 +172,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private ExpandableNotificationRow mNotificationRow; private ExpandableNotificationRow mBubbleNotificationRow; + private FakeShadeDialogContextInteractor mContextInteractor; private final Answer<Void> mCallOnDismiss = answerVoid( (OnDismissAction dismissAction, Runnable cancel, @@ -187,6 +189,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mDependency, TestableLooper.get(this)); + mContextInteractor = new FakeShadeDialogContextInteractor(mContext); + // Create standard notification with contentIntent mNotificationRow = notificationTestHelper.createRow(); StatusBarNotification sbn = mNotificationRow.getEntry().getSbn(); @@ -199,10 +203,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { bubbleSbn.getNotification().contentIntent = mContentIntent; bubbleSbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL; -// ArrayList<NotificationEntry> activeNotifications = new ArrayList<>(); -// activeNotifications.add(mNotificationRow.getEntry()); -// activeNotifications.add(mBubbleNotificationRow.getEntry()); -// when(mEntryManager.getVisibleNotifications()).thenReturn(activeNotifications); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mOnUserInteractionCallback.registerFutureDismissal(eq(mNotificationRow.getEntry()), anyInt())).thenReturn(mFutureDismissalRunnable); @@ -232,6 +232,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter = new StatusBarNotificationActivityStarter( getContext(), + mContextInteractor, mHandler, mUiBgExecutor, mVisibilityProvider, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt index d787e2c190c8..91404e0688ed 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.activityStarter import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.settings.userTracker +import com.android.systemui.shade.data.repository.shadeDialogContextInteractor import com.android.systemui.shade.domain.interactor.panelExpansionInteractor import com.android.systemui.shade.domain.interactor.shadeAnimationInteractor import com.android.systemui.shade.shadeController @@ -52,6 +53,7 @@ val Kosmos.statusBarNotificationActivityStarter by Kosmos.Fixture { StatusBarNotificationActivityStarter( applicationContext, + shadeDialogContextInteractor, fakeExecutorHandler, fakeExecutor, notificationVisibilityProvider, diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fd322a5c6345..7eebbba00778 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -162,6 +162,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.content.ComponentCallbacks; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; @@ -456,6 +457,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp private DisplayInfo mLastDisplayInfoOverride; private final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + @NonNull private final DisplayPolicy mDisplayPolicy; private final DisplayRotation mDisplayRotation; @@ -542,6 +544,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** Remove this display when animation on it has completed. */ private boolean mDeferredRemoval; + @NonNull final PinnedTaskController mPinnedTaskController; private final LinkedList<ActivityRecord> mTmpUpdateAllDrawn = new LinkedList(); @@ -1102,6 +1105,29 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp }; /** + * Called to update fields retrieve from {@link #getDisplayUiContext()} resources when + * there's a configuration update on {@link #getDisplayUiContext()}. + */ + @NonNull + private final ComponentCallbacks mSysUiContextConfigCallback = new ComponentCallbacks() { + + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + synchronized (mWmService.mGlobalLock) { + if (mDisplayReady) { + mDisplayPolicy.onConfigurationChanged(); + mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp(); + } + } + } + + @Override + public void onLowMemory() { + // Do nothing. + } + }; + + /** * Create new {@link DisplayContent} instance, add itself to the root window container and * initialize direct children. * @param display May not be null. @@ -2797,11 +2823,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final int lastOrientation = getConfiguration().orientation; final int lastWindowingMode = getWindowingMode(); super.onConfigurationChanged(newParentConfig); - if (mDisplayPolicy != null) { - mDisplayPolicy.onConfigurationChanged(); - mPinnedTaskController.onPostDisplayConfigurationChanged(); - mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp(); + if (!Flags.trackSystemUiContextBeforeWms()) { + mSysUiContextConfigCallback.onConfigurationChanged(newParentConfig); } + mPinnedTaskController.onPostDisplayConfigurationChanged(); // Update IME parent if needed. updateImeParent(); @@ -3381,6 +3406,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp .getKeyguardController().onDisplayRemoved(mDisplayId); mWallpaperController.resetLargestDisplay(mDisplay); mWmService.mDisplayWindowSettings.onDisplayRemoved(this); + if (Flags.trackSystemUiContextBeforeWms()) { + getDisplayUiContext().unregisterComponentCallbacks(mSysUiContextConfigCallback); + } } finally { mDisplayReady = false; } @@ -5429,7 +5457,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp reconfigureDisplayLocked(); onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration()); mWmService.mDisplayNotificationController.dispatchDisplayAdded(this); - // Attach the SystemUiContext to this DisplayContent the get latest configuration. + // Attach the SystemUiContext to this DisplayContent to get latest configuration. // Note that the SystemUiContext will be removed automatically if this DisplayContent // is detached. registerSystemUiContext(); @@ -5437,11 +5465,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } private void registerSystemUiContext() { + final Context systemUiContext = getDisplayUiContext(); final WindowProcessController wpc = mAtmService.getProcessController( - getDisplayUiContext().getIApplicationThread()); + systemUiContext.getIApplicationThread()); mWmService.mWindowContextListenerController.registerWindowContainerListener( - wpc, getDisplayUiContext().getWindowContextToken(), this, + wpc, systemUiContext.getWindowContextToken(), this, INVALID_WINDOW_TYPE, null /* options */); + if (Flags.trackSystemUiContextBeforeWms()) { + systemUiContext.registerComponentCallbacks(mSysUiContextConfigCallback); + } } @Override @@ -6620,6 +6652,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); }); } + @NonNull Context getDisplayUiContext() { return mDisplayPolicy.getSystemUiContext(); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 10f591cfd379..fbe850198c50 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1865,6 +1865,7 @@ public class DisplayPolicy { return mContext; } + @NonNull Context getSystemUiContext() { return mUiContext; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d699a689459e..0b20911bcab2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1820,125 +1820,138 @@ public class WindowManagerService extends IWindowManager.Stub final boolean hideSystemAlertWindows = shouldHideNonSystemOverlayWindow(win); win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows); - boolean imMayMove = true; - - win.mToken.addWindow(win); - displayPolicy.addWindowLw(win, attrs); - displayPolicy.setDropInputModePolicy(win, win.mAttrs); - if (type == TYPE_APPLICATION_STARTING && activity != null) { - activity.attachStartingWindow(win); - ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s", - activity, win); - } else if (type == TYPE_INPUT_METHOD - // IME window is always touchable. - // Ignore non-touchable windows e.g. Stylus InkWindow.java. - && (win.getAttrs().flags & FLAG_NOT_TOUCHABLE) == 0) { - displayContent.setInputMethodWindowLocked(win); - imMayMove = false; - } else if (type == TYPE_INPUT_METHOD_DIALOG) { - displayContent.computeImeTarget(true /* updateImeTarget */); - imMayMove = false; - } else { - if (type == TYPE_WALLPAPER) { - displayContent.mWallpaperController.clearLastWallpaperTimeoutTime(); - displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - } else if (win.hasWallpaper()) { - displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) { - // If there is currently a wallpaper being shown, and - // the base layer of the new window is below the current - // layer of the target window, then adjust the wallpaper. - // This is to avoid a new window being placed between the - // wallpaper and its target. - displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - } - } + res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState, + outAttachedFrame, outActiveControls, client, outSizeCompatScale, attrs); + } - final WindowStateAnimator winAnimator = win.mWinAnimator; - winAnimator.mEnterAnimationPending = true; - winAnimator.mEnteringAnimation = true; + Binder.restoreCallingIdentity(origId); - if (displayPolicy.areSystemBarsForcedConsumedLw()) { - res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS; - } - if (displayContent.isInTouchMode()) { - res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; - } - if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) { - res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; + return res; + } + + private int addWindowInner(@NonNull WindowState win, @NonNull DisplayPolicy displayPolicy, + @NonNull ActivityRecord activity, @NonNull DisplayContent displayContent, + @NonNull InsetsState outInsetsState, @NonNull Rect outAttachedFrame, + @NonNull InsetsSourceControl.Array outActiveControls, @NonNull IWindow client, + @NonNull float[] outSizeCompatScale, @NonNull LayoutParams attrs) { + int res = 0; + final int type = attrs.type; + boolean imMayMove = true; + + win.mToken.addWindow(win); + displayPolicy.addWindowLw(win, attrs); + displayPolicy.setDropInputModePolicy(win, win.mAttrs); + if (type == TYPE_APPLICATION_STARTING && activity != null) { + activity.attachStartingWindow(win); + ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s", + activity, win); + } else if (type == TYPE_INPUT_METHOD + // IME window is always touchable. + // Ignore non-touchable windows e.g. Stylus InkWindow.java. + && (win.getAttrs().flags & FLAG_NOT_TOUCHABLE) == 0) { + displayContent.setInputMethodWindowLocked(win); + imMayMove = false; + } else if (type == TYPE_INPUT_METHOD_DIALOG) { + displayContent.computeImeTarget(true /* updateImeTarget */); + imMayMove = false; + } else { + if (type == TYPE_WALLPAPER) { + displayContent.mWallpaperController.clearLastWallpaperTimeoutTime(); + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } else if (win.hasWallpaper()) { + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) { + // If there is currently a wallpaper being shown, and + // the base layer of the new window is below the current + // layer of the target window, then adjust the wallpaper. + // This is to avoid a new window being placed between the + // wallpaper and its target. + displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } + } - displayContent.getInputMonitor().setUpdateInputWindowsNeededLw(); + final WindowStateAnimator winAnimator = win.mWinAnimator; + winAnimator.mEnterAnimationPending = true; + winAnimator.mEnteringAnimation = true; - boolean focusChanged = false; - if (win.canReceiveKeys()) { - focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, - false /*updateInputWindows*/); - if (focusChanged) { - imMayMove = false; - } - } + if (displayPolicy.areSystemBarsForcedConsumedLw()) { + res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS; + } + if (displayContent.isInTouchMode()) { + res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; + } + if (win.mActivityRecord == null || win.mActivityRecord.isClientVisible()) { + res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE; + } - if (imMayMove) { - displayContent.computeImeTarget(true /* updateImeTarget */); - if (win.isImeOverlayLayeringTarget()) { - dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type, - win.isVisibleRequestedOrAdding(), false /* removed */, - displayContent.getDisplayId()); - } - } + displayContent.getInputMonitor().setUpdateInputWindowsNeededLw(); - // Don't do layout here, the window must call - // relayout to be displayed, so we'll do it there. - if (win.mActivityRecord != null && win.mActivityRecord.isEmbedded()) { - // Assign child layers from the parent Task if the Activity is embedded. - win.getTask().assignChildLayers(); - } else { - win.getParent().assignChildLayers(); + boolean focusChanged = false; + if (win.canReceiveKeys()) { + focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, + false /*updateInputWindows*/); + if (focusChanged) { + imMayMove = false; } + } - if (focusChanged) { - displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus, - false /*updateInputWindows*/); + if (imMayMove) { + displayContent.computeImeTarget(true /* updateImeTarget */); + if (win.isImeOverlayLayeringTarget()) { + dispatchImeTargetOverlayVisibilityChanged(client.asBinder(), win.mAttrs.type, + win.isVisibleRequestedOrAdding(), false /* removed */, + displayContent.getDisplayId()); } - displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); + } - ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s" - + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5)); + // Don't do layout here, the window must call + // relayout to be displayed, so we'll do it there. + if (win.mActivityRecord != null && win.mActivityRecord.isEmbedded()) { + // Assign child layers from the parent Task if the Activity is embedded. + win.getTask().assignChildLayers(); + } else { + win.getParent().assignChildLayers(); + } - boolean needToSendNewConfiguration = - win.isVisibleRequestedOrAdding() && displayContent.updateOrientation(); - if (win.providesDisplayDecorInsets()) { - needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo(); - } - if (needToSendNewConfiguration) { - displayContent.sendNewConfiguration(); - } + if (focusChanged) { + displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus, + false /*updateInputWindows*/); + } + displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); - // This window doesn't have a frame yet. Don't let this window cause the insets change. - displayContent.getInsetsStateController().updateAboveInsetsState( - false /* notifyInsetsChanged */); + ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s" + + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5)); - win.fillInsetsState(outInsetsState, true /* copySources */); - getInsetsSourceControls(win, outActiveControls); + boolean needToSendNewConfiguration = + win.isVisibleRequestedOrAdding() && displayContent.updateOrientation(); + if (win.providesDisplayDecorInsets()) { + needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo(); + } + if (needToSendNewConfiguration) { + displayContent.sendNewConfiguration(); + } - if (win.mLayoutAttached) { - outAttachedFrame.set(win.getParentWindow().getFrame()); - if (win.mInvGlobalScale != 1f) { - outAttachedFrame.scale(win.mInvGlobalScale); - } - } else { - // Make this invalid which indicates a null attached frame. - outAttachedFrame.set(0, 0, -1, -1); - } - outSizeCompatScale[0] = win.getCompatScaleForClient(); + // This window doesn't have a frame yet. Don't let this window cause the insets change. + displayContent.getInsetsStateController().updateAboveInsetsState( + false /* notifyInsetsChanged */); + + win.fillInsetsState(outInsetsState, true /* copySources */); + getInsetsSourceControls(win, outActiveControls); - if (res >= ADD_OKAY && win.isPresentation()) { - mPresentationController.onPresentationAdded(win); + if (win.mLayoutAttached) { + outAttachedFrame.set(win.getParentWindow().getFrame()); + if (win.mInvGlobalScale != 1f) { + outAttachedFrame.scale(win.mInvGlobalScale); } + } else { + // Make this invalid which indicates a null attached frame. + outAttachedFrame.set(0, 0, -1, -1); } + outSizeCompatScale[0] = win.getCompatScaleForClient(); - Binder.restoreCallingIdentity(origId); + if (res >= ADD_OKAY && win.isPresentation()) { + mPresentationController.onPresentationAdded(win); + } return res; } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java index 2d3f7231cc5c..ae5e85163e9a 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java @@ -866,6 +866,9 @@ public class InputMethodServiceTest { () -> mActivity.showImeWithWindowInsetsController(), EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown"); + eventually(() -> assertWithMessage("IME navigation bar is shown") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue()); + final var backButton = getUiObject(By.res(INPUT_METHOD_NAV_BACK_ID)); verifyInputViewStatus( () -> { @@ -892,6 +895,9 @@ public class InputMethodServiceTest { () -> mActivity.showImeWithWindowInsetsController(), EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown"); + eventually(() -> assertWithMessage("IME navigation bar is shown") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue()); + final var backButton = getUiObject(By.res(INPUT_METHOD_NAV_BACK_ID)); verifyInputViewStatus( () -> { @@ -927,6 +933,9 @@ public class InputMethodServiceTest { }, EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown"); + eventually(() -> assertWithMessage("IME navigation bar is shown") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue()); + final var imeSwitcherButton = getUiObject(By.res(INPUT_METHOD_NAV_IME_SWITCHER_ID)); imeSwitcherButton.click(); mInstrumentation.waitForIdleSync(); @@ -965,6 +974,9 @@ public class InputMethodServiceTest { }, EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown"); + eventually(() -> assertWithMessage("IME navigation bar is shown") + .that(mInputMethodService.isImeNavigationBarShownForTesting()).isTrue()); + final var imeSwitcherButton = getUiObject(By.res(INPUT_METHOD_NAV_IME_SWITCHER_ID)); imeSwitcherButton.longClick(); mInstrumentation.waitForIdleSync(); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 59335d3bb135..9406779c929d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -98,6 +98,7 @@ import com.android.server.policy.PermissionPolicyInternal; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.testutils.StubTransaction; import com.android.server.uri.UriGrantsManagerInternal; +import com.android.window.flags.Flags; import org.junit.rules.TestRule; import org.junit.runner.Description; @@ -657,6 +658,13 @@ public class SystemServicesTestRule implements TestRule { AppWarnings appWarnings = getAppWarningsLocked(); spyOn(appWarnings); doNothing().when(appWarnings).onStartActivity(any()); + + if (Flags.trackSystemUiContextBeforeWms()) { + final Context uiContext = getUiContext(); + spyOn(uiContext); + doNothing().when(uiContext).registerComponentCallbacks(any()); + doNothing().when(uiContext).unregisterComponentCallbacks(any()); + } } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index ccce57a81e41..b1525cf00dc6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -30,6 +30,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.content.res.Configuration; import android.graphics.Insets; import android.graphics.Rect; @@ -39,6 +40,7 @@ import android.view.DisplayCutout; import android.view.DisplayInfo; import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry; +import com.android.window.flags.Flags; class TestDisplayContent extends DisplayContent { @@ -79,6 +81,13 @@ class TestDisplayContent extends DisplayContent { WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget()); WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getTransientControlTarget()); + if (Flags.trackSystemUiContextBeforeWms()) { + final Context uiContext = getDisplayUiContext(); + spyOn(uiContext); + doNothing().when(uiContext).registerComponentCallbacks(any()); + doNothing().when(uiContext).unregisterComponentCallbacks(any()); + } + // For devices that set the sysprop ro.bootanim.set_orientation_<display_id> // See DisplayRotation#readDefaultDisplayRotation for context. // Without that, meaning of height and width in context of the tests can be swapped if diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 7f9e591ca5e3..97c6ac6854c3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -861,11 +861,9 @@ public class WindowTestsBase extends SystemServiceTestsBase { /** Creates a {@link DisplayContent} and adds it to the system. */ private DisplayContent createNewDisplay(DisplayInfo info, @DisplayImePolicy int imePolicy, @Nullable SettingsEntry overrideSettings) { - final DisplayContent display = - new TestDisplayContent.Builder(mAtm, info) - .setOverrideSettings(overrideSettings) - .build(); - final DisplayContent dc = display.mDisplayContent; + final DisplayContent dc = new TestDisplayContent.Builder(mAtm, info) + .setOverrideSettings(overrideSettings) + .build(); // this display can show IME. dc.mWmService.mDisplayWindowSettings.setDisplayImePolicy(dc, imePolicy); return dc; |