summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/aconfig/alarm.aconfig10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java24
-rw-r--r--api/StubLibraries.bp6
-rw-r--r--core/api/test-current.txt4
-rw-r--r--core/java/android/app/StatusBarManager.java16
-rw-r--r--core/java/android/app/UiAutomation.java9
-rw-r--r--core/java/android/app/supervision/ISupervisionManager.aidl1
-rw-r--r--core/java/android/app/supervision/SupervisionManager.java17
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java4
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig7
-rw-r--r--core/java/android/credentials/flags.aconfig1
-rw-r--r--core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java38
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java24
-rw-r--r--core/java/android/hardware/camera2/params/SharedSessionConfiguration.java2
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java16
-rw-r--r--core/java/android/inputmethodservice/navigationbar/NavigationBarView.java7
-rw-r--r--core/java/android/widget/RemoteViews.java11
-rw-r--r--core/java/android/window/DesktopModeFlags.java4
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig10
-rw-r--r--core/tests/FileSystemUtilsTest/Android.bp38
-rw-r--r--core/tests/FileSystemUtilsTest/AndroidTest.xml1
-rw-r--r--core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml1
-rw-r--r--core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_no_override.xml37
-rw-r--r--core/tests/FileSystemUtilsTest/app_with_4kb_elf/page_size_compat_disabled.xml36
-rw-r--r--core/tests/FileSystemUtilsTest/app_with_4kb_elf/res/layout/hello.xml28
-rw-r--r--core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java3
-rw-r--r--core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java37
-rw-r--r--core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java54
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/compatui/DesktopModeCompatPolicy.kt)32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java104
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java199
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt90
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java34
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt105
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt3
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt3
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java29
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt77
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt92
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt (renamed from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/DesktopModeCompatPolicyTest.kt)3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt20
-rw-r--r--media/java/android/media/AudioManager.java32
-rw-r--r--media/java/android/media/quality/MediaQualityContract.java12
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java36
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout/settingslib_number_button.xml3
-rw-r--r--packages/SettingsLib/ButtonPreference/res/layout/settingslib_section_button.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java19
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java17
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java34
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java35
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig7
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt25
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt17
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt9
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt3
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt40
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt61
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java57
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt2
-rw-r--r--packages/SystemUI/res/drawable/notif_footer_btn_background.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl7
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java42
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java29
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java18
-rw-r--r--packages/SystemUI/src/com/android/keyguard/CarrierText.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt20
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java (renamed from services/accessibility/java/com/android/server/accessibility/AutoclickController.java)7
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java (renamed from services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java)2
-rw-r--r--services/core/java/com/android/server/audio/AudioManagerShellCommand.java13
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java41
-rw-r--r--services/core/java/com/android/server/display/plugin/PluginManager.java10
-rw-r--r--services/core/java/com/android/server/display/plugin/PluginStorage.java200
-rw-r--r--services/core/java/com/android/server/input/InputGestureManager.java4
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionStopController.java32
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java177
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java145
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java16
-rw-r--r--services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java5
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java2
-rw-r--r--services/core/java/com/android/server/wm/Transition.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java21
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionService.java96
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java12
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionUserData.java2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt9
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt75
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java26
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java68
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java (renamed from services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java)4
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java107
-rw-r--r--services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java86
-rw-r--r--services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt50
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java39
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java4
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java4
161 files changed, 2869 insertions, 918 deletions
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
index a6e980726a9a..9181ef0c532a 100644
--- a/apex/jobscheduler/service/aconfig/alarm.aconfig
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -7,3 +7,13 @@ flag {
description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due"
bug: "314907186"
}
+
+flag {
+ name: "acquire_wakelock_before_send"
+ namespace: "backstage_power"
+ description: "Acquire the userspace alarm wakelock before sending the alarm"
+ bug: "391413964"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 829442aed6ac..f89b13dce307 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -5334,6 +5334,18 @@ public class AlarmManagerService extends SystemService {
public void deliverLocked(Alarm alarm, long nowELAPSED) {
final long workSourceToken = ThreadLocalWorkSource.setUid(
getAlarmAttributionUid(alarm));
+
+ if (Flags.acquireWakelockBeforeSend()) {
+ // Acquire the wakelock before starting the app. This needs to be done to avoid
+ // random stalls in the receiving app in case a suspend attempt is already in
+ // progress. See b/391413964 for an incident where this was found to happen.
+ if (mBroadcastRefCount == 0) {
+ setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true);
+ mWakeLock.acquire();
+ mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1, 0).sendToTarget();
+ }
+ }
+
try {
if (alarm.operation != null) {
// PendingIntent alarm
@@ -5399,14 +5411,16 @@ public class AlarmManagerService extends SystemService {
ThreadLocalWorkSource.restore(workSourceToken);
}
- // The alarm is now in flight; now arrange wakelock and stats tracking
if (DEBUG_WAKELOCK) {
Slog.d(TAG, "mBroadcastRefCount -> " + (mBroadcastRefCount + 1));
}
- if (mBroadcastRefCount == 0) {
- setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true);
- mWakeLock.acquire();
- mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1, 0).sendToTarget();
+ if (!Flags.acquireWakelockBeforeSend()) {
+ // The alarm is now in flight; now arrange wakelock and stats tracking
+ if (mBroadcastRefCount == 0) {
+ setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true);
+ mWakeLock.acquire();
+ mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1, 0).sendToTarget();
+ }
}
final InFlight inflight = new InFlight(AlarmManagerService.this, alarm, nowELAPSED);
mInFlight.add(inflight);
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 787fdee6ee16..3314b4aa0d71 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -648,7 +648,7 @@ java_api_library {
java_api_library {
name: "android-non-updatable.stubs.module_lib.from-text",
- api_surface: "module_lib",
+ api_surface: "module-lib",
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
@@ -668,7 +668,7 @@ java_api_library {
// generated from this module, as this module is strictly used for hiddenapi only.
java_api_library {
name: "android-non-updatable.stubs.test_module_lib",
- api_surface: "module_lib",
+ api_surface: "module-lib",
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
@@ -689,7 +689,7 @@ java_api_library {
java_api_library {
name: "android-non-updatable.stubs.system_server.from-text",
- api_surface: "system_server",
+ api_surface: "system-server",
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f06bd48a8cd8..e2fe5062d356 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1634,6 +1634,10 @@ package android.hardware.camera2.params {
method public void setColorSpace(@NonNull android.graphics.ColorSpace.Named);
}
+ @FlaggedApi("com.android.internal.camera.flags.camera_multi_client") public final class SharedSessionConfiguration {
+ ctor public SharedSessionConfiguration(int, @NonNull long[]);
+ }
+
}
package android.hardware.devicestate {
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 7454c44997d6..039f7b6a86ba 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -211,13 +211,13 @@ public class StatusBarManager {
*
* @hide
*/
- public static final int NAVIGATION_HINT_IME_SHOWN = 1 << 1;
+ public static final int NAVIGATION_HINT_IME_VISIBLE = 1 << 1;
/**
* The IME Switcher button is visible. This only takes effect while the IME is visible.
*
* @hide
*/
- public static final int NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN = 1 << 2;
+ public static final int NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE = 1 << 2;
/**
* Navigation bar flags related to the IME state.
*
@@ -225,8 +225,8 @@ public class StatusBarManager {
*/
@IntDef(flag = true, prefix = { "NAVIGATION_HINT_" }, value = {
NAVIGATION_HINT_BACK_ALT,
- NAVIGATION_HINT_IME_SHOWN,
- NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN,
+ NAVIGATION_HINT_IME_VISIBLE,
+ NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationHint {}
@@ -1360,11 +1360,11 @@ public class StatusBarManager {
if ((hints & NAVIGATION_HINT_BACK_ALT) != 0) {
hintStrings.add("NAVIGATION_HINT_BACK_ALT");
}
- if ((hints & NAVIGATION_HINT_IME_SHOWN) != 0) {
- hintStrings.add("NAVIGATION_HINT_IME_SHOWN");
+ if ((hints & NAVIGATION_HINT_IME_VISIBLE) != 0) {
+ hintStrings.add("NAVIGATION_HINT_IME_VISIBLE");
}
- if ((hints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0) {
- hintStrings.add("NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN");
+ if ((hints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE) != 0) {
+ hintStrings.add("NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE");
}
return String.join(" | ", hintStrings);
}
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 1035d8b93881..063055eb4917 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1146,9 +1146,9 @@ public final class UiAutomation {
executionStartTimeMillis = SystemClock.uptimeMillis();
eventQueueStartIndex = mEventQueue.size();
}
- if (DEBUG) {
- Log.d(LOG_TAG, "executeAndWaitForEvent: watchersCount=" + watchersDepth
- + ", eventQueueStartIndex=" + eventQueueStartIndex);
+ if (VERBOSE) {
+ Log.v(LOG_TAG, "executeAndWaitForEvent starts at depth=" + watchersDepth + ", "
+ + "command=" + command + ", filter=" + filter + ", timeout=" + timeoutMillis);
}
try {
@@ -1199,6 +1199,9 @@ public final class UiAutomation {
}
mLock.notifyAll();
}
+ if (VERBOSE) {
+ Log.v(LOG_TAG, "executeAndWaitForEvent ends at depth=" + watchersDepth);
+ }
}
}
diff --git a/core/java/android/app/supervision/ISupervisionManager.aidl b/core/java/android/app/supervision/ISupervisionManager.aidl
index 4598421eb3bc..c3f3b1ced33c 100644
--- a/core/java/android/app/supervision/ISupervisionManager.aidl
+++ b/core/java/android/app/supervision/ISupervisionManager.aidl
@@ -22,4 +22,5 @@ package android.app.supervision;
*/
interface ISupervisionManager {
boolean isSupervisionEnabledForUser(int userId);
+ String getActiveSupervisionAppPackage(int userId);
}
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
index 92241f3634e8..12432ddd0eb9 100644
--- a/core/java/android/app/supervision/SupervisionManager.java
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -16,6 +16,7 @@
package android.app.supervision;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.UserHandleAware;
@@ -98,4 +99,20 @@ public class SupervisionManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns the package name of the app that is acting as the active supervision app or null if
+ * supervision is disabled.
+ *
+ * @hide
+ */
+ @UserHandleAware
+ @Nullable
+ public String getActiveSupervisionAppPackage() {
+ try {
+ return mService.getActiveSupervisionAppPackage(mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index df1028e9e04c..b9b5c6a8bbc3 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityOptions;
-import android.app.LoadedApk;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -753,9 +752,6 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW
*/
protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
try {
- ApplicationInfo expectedAppInfo = mInfo.providerInfo.applicationInfo;
- LoadedApk.checkAndUpdateApkPaths(expectedAppInfo);
- // Return if cloned successfully, otherwise default
Context newContext = mContext.createApplicationContext(
mInfo.providerInfo.applicationInfo,
Context.CONTEXT_RESTRICTED);
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 6da2a073ec19..1cf42820f356 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -18,13 +18,6 @@ flag {
}
flag {
- namespace: "virtual_devices"
- name: "media_projection_keyguard_restrictions"
- description: "Auto-stop MP when the device locks"
- bug: "348335290"
-}
-
-flag {
namespace: "virtual_devices"
name: "virtual_display_insets"
description: "APIs for specifying virtual display insets (via cutout)"
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index 430ed2b68342..449423f1ea1f 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -133,6 +133,7 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
+ is_exported: true
}
flag {
diff --git a/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java b/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
index de93234445ca..d3fb93588762 100644
--- a/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
+++ b/core/java/android/hardware/biometrics/ParentalControlsUtilsInternal.java
@@ -19,6 +19,7 @@ package android.hardware.biometrics;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
+import android.app.supervision.SupervisionManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
@@ -55,27 +56,44 @@ public class ParentalControlsUtilsInternal {
return null;
}
- public static boolean parentConsentRequired(@NonNull Context context,
- @NonNull DevicePolicyManager dpm, @BiometricAuthenticator.Modality int modality,
+ /** @return true if parental consent is required in order for biometric sensors to be used. */
+ public static boolean parentConsentRequired(
+ @NonNull Context context,
+ @NonNull DevicePolicyManager dpm,
+ @Nullable SupervisionManager sm,
+ @BiometricAuthenticator.Modality int modality,
@NonNull UserHandle userHandle) {
if (getTestComponentName(context, userHandle.getIdentifier()) != null) {
return true;
}
- return parentConsentRequired(dpm, modality, userHandle);
+ return parentConsentRequired(dpm, sm, modality, userHandle);
}
/**
* @return true if parental consent is required in order for biometric sensors to be used.
*/
- public static boolean parentConsentRequired(@NonNull DevicePolicyManager dpm,
- @BiometricAuthenticator.Modality int modality, @NonNull UserHandle userHandle) {
- final ComponentName cn = getSupervisionComponentName(dpm, userHandle);
- if (cn == null) {
- return false;
+ public static boolean parentConsentRequired(
+ @NonNull DevicePolicyManager dpm,
+ @Nullable SupervisionManager sm,
+ @BiometricAuthenticator.Modality int modality,
+ @NonNull UserHandle userHandle) {
+ final int keyguardDisabledFeatures;
+
+ if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) {
+ if (sm != null && !sm.isSupervisionEnabledForUser(userHandle.getIdentifier())) {
+ return false;
+ }
+ // Check for keyguard features disabled by any admin.
+ keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(/* admin= */ null);
+ } else {
+ final ComponentName cn = getSupervisionComponentName(dpm, userHandle);
+ if (cn == null) {
+ return false;
+ }
+ keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(cn);
}
- final int keyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(cn);
final boolean dpmFpDisabled = containsFlag(keyguardDisabledFeatures,
DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
final boolean dpmFaceDisabled = containsFlag(keyguardDisabledFeatures,
@@ -97,7 +115,9 @@ public class ParentalControlsUtilsInternal {
return consentRequired;
}
+ /** @deprecated Use {@link SupervisionManager} to check for supervision. */
@Nullable
+ @Deprecated
public static ComponentName getSupervisionComponentName(@NonNull DevicePolicyManager dpm,
@NonNull UserHandle userHandle) {
return dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle);
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 5533a640b9d8..210653bb41e5 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -5256,9 +5256,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>DYNAMIC_RANGE_PROFILE: {STANDARD, HLG10}</p>
* </li>
* </ul>
- * <p>All of the above configurations can be set up with a SessionConfiguration. The list of
- * OutputConfiguration contains the stream configurations and DYNAMIC_RANGE_PROFILE, and
- * the AE_TARGET_FPS_RANGE and VIDEO_STABILIZATION_MODE are set as session parameters.</p>
* <p>When set to BAKLAVA, the additional stream combinations below are verified
* by the compliance tests:</p>
* <table>
@@ -5268,6 +5265,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <th style="text-align: center;">Size</th>
* <th style="text-align: center;">Target 2</th>
* <th style="text-align: center;">Size</th>
+ * <th style="text-align: center;">Target 3</th>
+ * <th style="text-align: center;">Size</th>
* </tr>
* </thead>
* <tbody>
@@ -5276,15 +5275,34 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <td style="text-align: center;">S1080P</td>
* <td style="text-align: center;">PRIV</td>
* <td style="text-align: center;">S1080P</td>
+ * <td style="text-align: center;"></td>
+ * <td style="text-align: center;"></td>
* </tr>
* <tr>
* <td style="text-align: center;">PRIV</td>
* <td style="text-align: center;">S1080P</td>
* <td style="text-align: center;">PRIV</td>
* <td style="text-align: center;">S1440P</td>
+ * <td style="text-align: center;"></td>
+ * <td style="text-align: center;"></td>
+ * </tr>
+ * <tr>
+ * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">S1080P</td>
+ * <td style="text-align: center;">YUV</td>
+ * <td style="text-align: center;">S1080P</td>
+ * <td style="text-align: center;">S1080P</td>
+ * <td style="text-align: center;">PRIV</td>
* </tr>
* </tbody>
* </table>
+ * <ul>
+ * <li>VIDEO_STABILIZATION_MODE: {OFF, ON} for the newly added stream combinations given the
+ * presence of dedicated video stream</li>
+ * </ul>
+ * <p>All of the above configurations can be set up with a SessionConfiguration. The list of
+ * OutputConfiguration contains the stream configurations and DYNAMIC_RANGE_PROFILE, and
+ * the AE_TARGET_FPS_RANGE and VIDEO_STABILIZATION_MODE are set as session parameters.</p>
* <p>This key is available on all devices.</p>
*/
@PublicKey
diff --git a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
index 365f870ba22d..b40c7d35c06b 100644
--- a/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SharedSessionConfiguration.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.graphics.ColorSpace;
import android.graphics.ImageFormat.Format;
import android.hardware.DataSpace.NamedDataSpace;
@@ -228,6 +229,7 @@ public final class SharedSessionConfiguration {
*
* @hide
*/
+ @TestApi
public SharedSessionConfiguration(int sharedColorSpace,
@NonNull long[] sharedOutputConfigurations) {
mColorSpace = sharedColorSpace;
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index edc5cb7cdf6a..8ca139fdf9b9 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -17,8 +17,8 @@
package android.inputmethodservice;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_VISIBLE;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE;
import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -242,10 +242,10 @@ final class NavigationBarController {
NavigationBarView.class::isInstance);
if (navigationBarView != null) {
// TODO(b/213337792): Support InputMethodService#setBackDisposition().
- // TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
- final int hints = NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
+ // TODO(b/213337792): Set NAVIGATION_HINT_IME_VISIBLE only when necessary.
+ final int hints = NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_VISIBLE
| (mShouldShowImeSwitcherWhenImeIsShown
- ? NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN : 0);
+ ? NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE : 0);
navigationBarView.setNavigationIconHints(hints);
navigationBarView.prepareNavButtons(this);
}
@@ -515,10 +515,10 @@ final class NavigationBarController {
NavigationBarView.class::isInstance);
if (navigationBarView != null) {
// TODO(b/213337792): Support InputMethodService#setBackDisposition().
- // TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
- final int hints = NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
+ // TODO(b/213337792): Set NAVIGATION_HINT_IME_VISIBLE only when necessary.
+ final int hints = NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_VISIBLE
| (mShouldShowImeSwitcherWhenImeIsShown
- ? NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN : 0);
+ ? NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE : 0);
navigationBarView.setNavigationIconHints(hints);
}
} else {
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
index 622d5d1b1c5a..5c0977115e36 100644
--- a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
@@ -17,7 +17,7 @@
package android.inputmethodservice.navigationbar;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE;
import static android.inputmethodservice.navigationbar.NavigationBarConstants.DARK_MODE_ICON_COLOR_SINGLE_TONE;
import static android.inputmethodservice.navigationbar.NavigationBarConstants.LIGHT_MODE_ICON_COLOR_SINGLE_TONE;
import static android.inputmethodservice.navigationbar.NavigationBarConstants.NAVBAR_BACK_BUTTON_IME_OFFSET;
@@ -316,9 +316,10 @@ public final class NavigationBarView extends FrameLayout {
getImeSwitchButton().setImageDrawable(mImeSwitcherIcon);
- // Update IME button visibility, a11y and rotate button always overrides the appearance
+ // Update IME switcher button visibility, a11y and rotate button always overrides
+ // the appearance.
final boolean isImeSwitcherButtonVisible =
- (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0;
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE) != 0;
getImeSwitchButton()
.setVisibility(isImeSwitcherButtonVisible ? View.VISIBLE : View.INVISIBLE);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7c75d7b30037..0e329c2859db 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -44,7 +44,6 @@ import android.app.Activity;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
-import android.app.LoadedApk;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.appwidget.AppWidgetHostView;
@@ -8484,8 +8483,14 @@ public class RemoteViews implements Parcelable, Filter {
return context;
}
try {
- LoadedApk.checkAndUpdateApkPaths(mApplication);
- Context applicationContext = context.createApplicationContext(mApplication,
+ // Use PackageManager as the source of truth for application information, rather
+ // than the parceled ApplicationInfo provided by the app.
+ ApplicationInfo sanitizedApplication =
+ context.getPackageManager().getApplicationInfoAsUser(
+ mApplication.packageName, 0,
+ UserHandle.getUserId(mApplication.uid));
+ Context applicationContext = context.createApplicationContext(
+ sanitizedApplication,
Context.CONTEXT_RESTRICTED);
// Get the correct apk paths while maintaining the current context's configuration.
return applicationContext.createConfigurationContext(
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index e51ef4f6d04c..82055afda8c1 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -94,7 +94,9 @@ public enum DesktopModeFlags {
Flags::enableDesktopAppLaunchTransitionsBugfix, false),
INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true),
- ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true);
+ ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true),
+ ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
+ ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS(Flags::enableTaskResizingKeyboardShortcuts, true);
/**
* Flag class, to be used in case the enum cannot be used because the flag is not accessible.
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index e35c3b80a58b..222088e8a8b9 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -583,3 +583,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_start_launch_transition_from_taskbar_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables starting a launch transition directly from the taskbar if desktop tasks are visible."
+ bug: "361366053"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/tests/FileSystemUtilsTest/Android.bp b/core/tests/FileSystemUtilsTest/Android.bp
index 962ff3c0a6e0..f4d92522bb25 100644
--- a/core/tests/FileSystemUtilsTest/Android.bp
+++ b/core/tests/FileSystemUtilsTest/Android.bp
@@ -36,10 +36,10 @@ cc_library {
ldflags: ["-z max-page-size=0x1000"],
}
-android_test_helper_app {
- name: "app_with_4kb_elf",
+java_defaults {
+ name: "app_with_4kb_elf_defaults",
srcs: ["app_with_4kb_elf/src/**/*.java"],
- manifest: "app_with_4kb_elf/app_with_4kb_elf.xml",
+ resource_dirs: ["app_with_4kb_elf/res"],
compile_multilib: "64",
jni_libs: [
"libpunchtest_4kb",
@@ -47,7 +47,36 @@ android_test_helper_app {
static_libs: [
"androidx.test.rules",
"platform-test-annotations",
+ "androidx.test.uiautomator_uiautomator",
+ "sysui-helper",
],
+}
+
+android_test_helper_app {
+ name: "app_with_4kb_elf",
+ defaults: ["app_with_4kb_elf_defaults"],
+ manifest: "app_with_4kb_elf/app_with_4kb_elf.xml",
+ use_embedded_native_libs: true,
+}
+
+android_test_helper_app {
+ name: "app_with_4kb_compressed_elf",
+ defaults: ["app_with_4kb_elf_defaults"],
+ manifest: "app_with_4kb_elf/app_with_4kb_elf.xml",
+ use_embedded_native_libs: false,
+}
+
+android_test_helper_app {
+ name: "page_size_compat_disabled_app",
+ defaults: ["app_with_4kb_elf_defaults"],
+ manifest: "app_with_4kb_elf/page_size_compat_disabled.xml",
+ use_embedded_native_libs: true,
+}
+
+android_test_helper_app {
+ name: "app_with_4kb_elf_no_override",
+ defaults: ["app_with_4kb_elf_defaults"],
+ manifest: "app_with_4kb_elf/app_with_4kb_no_override.xml",
use_embedded_native_libs: true,
}
@@ -99,6 +128,9 @@ java_test_host {
":embedded_native_libs_test_app",
":extract_native_libs_test_app",
":app_with_4kb_elf",
+ ":page_size_compat_disabled_app",
+ ":app_with_4kb_compressed_elf",
+ ":app_with_4kb_elf_no_override",
],
test_suites: ["general-tests"],
test_config: "AndroidTest.xml",
diff --git a/core/tests/FileSystemUtilsTest/AndroidTest.xml b/core/tests/FileSystemUtilsTest/AndroidTest.xml
index 651a7ca15dac..27f49b2289ba 100644
--- a/core/tests/FileSystemUtilsTest/AndroidTest.xml
+++ b/core/tests/FileSystemUtilsTest/AndroidTest.xml
@@ -22,7 +22,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="embedded_native_libs_test_app.apk" />
<option name="test-file-name" value="extract_native_libs_test_app.apk" />
- <option name="test-file-name" value="app_with_4kb_elf.apk" />
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml
index b9d6d4db2c81..d7a37336cbc3 100644
--- a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml
@@ -18,7 +18,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.test.pagesizecompat">
<application
- android:extractNativeLibs="false"
android:pageSizeCompat="enabled">
<uses-library android:name="android.test.runner"/>
<activity android:name=".MainActivity"
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_no_override.xml b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_no_override.xml
new file mode 100644
index 000000000000..b0b5204d6e80
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_no_override.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.pagesizecompat">
+ <application
+ android:label="PageSizeCompatTestApp">
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".MainActivity"
+ android:exported="true"
+ android:label="Home page"
+ android:process=":NewProcess">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.pagesizecompat"/>
+</manifest> \ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/page_size_compat_disabled.xml b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/page_size_compat_disabled.xml
new file mode 100644
index 000000000000..641c5e741014
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/page_size_compat_disabled.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.pagesizecompat">
+ <application
+ android:pageSizeCompat="disabled">
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".MainActivity"
+ android:exported="true"
+ android:process=":NewProcess">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.pagesizecompat"/>
+</manifest> \ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/res/layout/hello.xml b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/res/layout/hello.xml
new file mode 100644
index 000000000000..473f3f9f9402
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/res/layout/hello.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:keepScreenOn="true">
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="this is a test activity"
+ />
+</LinearLayout>
+
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java
index 893f9cd01497..5d8d8081b0e5 100644
--- a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java
@@ -19,6 +19,7 @@ package android.test.pagesizecompat;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
+import android.view.View;
import androidx.annotation.VisibleForTesting;
@@ -43,6 +44,8 @@ public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedOnstanceState) {
super.onCreate(savedOnstanceState);
+ View view = getLayoutInflater().inflate(R.layout.hello, null);
+ setContentView(view);
Intent received = getIntent();
int op1 = received.getIntExtra(KEY_OPERAND_1, -1);
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java
index 9cbe414a0993..7d05c64f7624 100644
--- a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java
@@ -16,6 +16,8 @@
package android.test.pagesizecompat;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -23,6 +25,10 @@ import android.content.IntentFilter;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
import org.junit.Assert;
import org.junit.Test;
@@ -33,9 +39,10 @@ import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class PageSizeCompatTest {
+ private static final String WARNING_TEXT = "PageSizeCompatTestApp";
+ private static final long TIMEOUT = 5000;
- @Test
- public void testPageSizeCompat_embedded4KbLib() throws Exception {
+ public void testPageSizeCompat_appLaunch(boolean shouldPass) throws Exception {
Context context = InstrumentationRegistry.getContext();
CountDownLatch receivedSignal = new CountDownLatch(1);
@@ -62,6 +69,30 @@ public class PageSizeCompatTest {
launchIntent.putExtra(MainActivity.KEY_OPERAND_2, op2);
context.startActivity(launchIntent);
- Assert.assertTrue("Failed to launch app", receivedSignal.await(10, TimeUnit.SECONDS));
+ UiDevice device = UiDevice.getInstance(getInstrumentation());
+ device.waitForWindowUpdate(null, TIMEOUT);
+
+ Assert.assertEquals(receivedSignal.await(10, TimeUnit.SECONDS), shouldPass);
+ }
+
+ @Test
+ public void testPageSizeCompat_compatEnabled() throws Exception {
+ testPageSizeCompat_appLaunch(true);
+ }
+
+ @Test
+ public void testPageSizeCompat_compatDisabled() throws Exception {
+ testPageSizeCompat_appLaunch(false);
+ }
+
+ @Test
+ public void testPageSizeCompat_compatByAlignmentChecks() throws Exception {
+ testPageSizeCompat_appLaunch(true);
+
+ //verify warning dialog
+ UiDevice device = UiDevice.getInstance(getInstrumentation());
+ device.waitForWindowUpdate(null, TIMEOUT);
+ UiObject2 targetObject = device.wait(Until.findObject(By.text(WARNING_TEXT)), TIMEOUT);
+ Assert.assertTrue(targetObject != null);
}
}
diff --git a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
index aed907a0242f..208d74e49afe 100644
--- a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
+++ b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
@@ -17,10 +17,12 @@
package com.android.internal.content;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import android.platform.test.annotations.AppModeFull;
import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.targetprep.TargetSetupError;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -29,6 +31,12 @@ import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
public class FileSystemUtilsTest extends BaseHostJUnit4Test {
+ private static final String PAGE_SIZE_COMPAT_ENABLED = "app_with_4kb_elf.apk";
+ private static final String PAGE_SIZE_COMPAT_DISABLED = "page_size_compat_disabled_app.apk";
+ private static final String PAGE_SIZE_COMPAT_ENABLED_COMPRESSED_ELF =
+ "app_with_4kb_compressed_elf.apk";
+ private static final String PAGE_SIZE_COMPAT_ENABLED_BY_PLATFORM =
+ "app_with_4kb_elf_no_override.apk";
@Test
@AppModeFull
@@ -48,12 +56,50 @@ public class FileSystemUtilsTest extends BaseHostJUnit4Test {
runDeviceTests(appPackage, appPackage + "." + testName);
}
- @Test
- @AppModeFull
- public void runAppWith4KbLib_overrideCompatMode() throws DeviceNotAvailableException {
+ private void runPageSizeCompatTest(String appName, String testMethodName)
+ throws DeviceNotAvailableException, TargetSetupError {
+ getDevice().enableAdbRoot();
+ String result = getDevice().executeShellCommand("getconf PAGE_SIZE");
+ assumeTrue("16384".equals(result.strip()));
+ installPackage(appName, "-r");
String appPackage = "android.test.pagesizecompat";
String testName = "PageSizeCompatTest";
assertTrue(isPackageInstalled(appPackage));
- runDeviceTests(appPackage, appPackage + "." + testName);
+ assertTrue(runDeviceTests(appPackage, appPackage + "." + testName,
+ testMethodName));
+ uninstallPackage(appPackage);
+ }
+
+ @Test
+ @AppModeFull
+ public void runAppWith4KbLib_overrideCompatMode()
+ throws DeviceNotAvailableException, TargetSetupError {
+ runPageSizeCompatTest(PAGE_SIZE_COMPAT_ENABLED, "testPageSizeCompat_compatEnabled");
+ }
+
+ @Test
+ @AppModeFull
+ public void runAppWith4KbCompressedLib_overrideCompatMode()
+ throws DeviceNotAvailableException, TargetSetupError {
+ runPageSizeCompatTest(PAGE_SIZE_COMPAT_ENABLED_COMPRESSED_ELF,
+ "testPageSizeCompat_compatEnabled");
+ }
+
+ @Test
+ @AppModeFull
+ public void runAppWith4KbLib_disabledCompatMode()
+ throws DeviceNotAvailableException, TargetSetupError {
+ // This test is expected to fail since compat is disabled in manifest
+ runPageSizeCompatTest(PAGE_SIZE_COMPAT_DISABLED,
+ "testPageSizeCompat_compatDisabled");
+ }
+
+ @Test
+ @AppModeFull
+ public void runAppWith4KbLib_compatByAlignmentChecks()
+ throws DeviceNotAvailableException, TargetSetupError {
+ // This test is expected to fail since compat is disabled in manifest
+ runPageSizeCompatTest(PAGE_SIZE_COMPAT_ENABLED_BY_PLATFORM,
+ "testPageSizeCompat_compatByAlignmentChecks");
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/DesktopModeCompatPolicy.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
index e55af112cd5f..0ea3c2a80fb4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/DesktopModeCompatPolicy.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
@@ -14,20 +14,18 @@
* limitations under the License.
*/
-package com.android.wm.shell.compatui
+package com.android.wm.shell.shared.desktopmode
-import android.app.ActivityManager.RunningTaskInfo
+import android.app.TaskInfo
import android.content.Context
+import android.window.DesktopModeFlags
import com.android.internal.R
-import com.android.wm.shell.dagger.WMSingleton
-import javax.inject.Inject
/**
* Class to decide whether to apply app compat policies in desktop mode.
*/
// TODO(b/347289970): Consider replacing with API
-@WMSingleton
-class DesktopModeCompatPolicy @Inject constructor(context: Context) {
+class DesktopModeCompatPolicy(context: Context) {
private val systemUiPackage: String = context.resources.getString(R.string.config_systemUi)
@@ -37,16 +35,26 @@ class DesktopModeCompatPolicy @Inject constructor(context: Context) {
* not being displayed, regardless of its configuration, we will not exempt it as to remain in
* the desktop windowing environment.
*/
- fun isTopActivityExemptFromDesktopWindowing(task: RunningTaskInfo) =
- (isSystemUiTask(task) || isTransparentTask(task)) && !task.isTopActivityNoDisplay
+ fun isTopActivityExemptFromDesktopWindowing(task: TaskInfo) =
+ isTopActivityExemptFromDesktopWindowing(task.baseActivity?.packageName,
+ task.numActivities, task.isTopActivityNoDisplay, task.isActivityStackTransparent)
+
+ fun isTopActivityExemptFromDesktopWindowing(packageName: String?,
+ numActivities: Int, isTopActivityNoDisplay: Boolean, isActivityStackTransparent: Boolean) =
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue
+ && ((isSystemUiTask(packageName)
+ || isTransparentTask(isActivityStackTransparent, numActivities))
+ && !isTopActivityNoDisplay)
/**
* Returns true if all activities in a tasks stack are transparent. If there are no activities
* will return false.
*/
- fun isTransparentTask(task: RunningTaskInfo): Boolean = task.isActivityStackTransparent
- && task.numActivities > 0
+ fun isTransparentTask(task: TaskInfo): Boolean =
+ isTransparentTask(task.isActivityStackTransparent, task.numActivities)
+
+ private fun isTransparentTask(isActivityStackTransparent: Boolean, numActivities: Int) =
+ isActivityStackTransparent && numActivities > 0
- private fun isSystemUiTask(task: RunningTaskInfo): Boolean =
- task.baseActivity?.packageName == systemUiPackage
+ private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index e68a98fb7f21..a65e69eee5fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -542,7 +542,7 @@ public class Bubble implements BubbleViewProvider {
return (mMetadataShortcutId != null && !mMetadataShortcutId.isEmpty());
}
- BubbleTransitions.BubbleTransition getPreparingTransition() {
+ public BubbleTransitions.BubbleTransition getPreparingTransition() {
return mPreparingTransition;
}
@@ -572,7 +572,8 @@ public class Bubble implements BubbleViewProvider {
mIntentActive = false;
}
- private void cleanupTaskView() {
+ /** Cleans-up the taskview associated with this bubble (possibly removing the task from wm) */
+ public void cleanupTaskView() {
if (mBubbleTaskView != null) {
mBubbleTaskView.cleanup();
mBubbleTaskView = null;
@@ -593,7 +594,7 @@ public class Bubble implements BubbleViewProvider {
* <p>If we're switching between bar and floating modes, pass {@code false} on
* {@code cleanupTaskView} to avoid recreating it in the new mode.
*/
- void cleanupViews(boolean cleanupTaskView) {
+ public void cleanupViews(boolean cleanupTaskView) {
cleanupExpandedView(cleanupTaskView);
mIconView = null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 1f463295233f..5f2b95f7b137 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -40,9 +40,11 @@ import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.PendingIntent;
+import android.app.TaskInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -78,6 +80,8 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.ScreenCapture;
import android.window.ScreenCapture.SynchronousScreenCaptureListener;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -360,7 +364,7 @@ public class BubbleController implements ConfigurationChangeListener,
} else {
tvTransitions = taskViewTransitions;
}
- mTaskViewController = tvTransitions;
+ mTaskViewController = new BubbleTaskViewController(tvTransitions);
mBubbleTransitions = new BubbleTransitions(transitions, organizer, taskViewRepository, data,
tvTransitions, context);
mTransitions = transitions;
@@ -2076,7 +2080,12 @@ public class BubbleController implements ConfigurationChangeListener,
@Override
public void removeBubble(Bubble removedBubble) {
if (mLayerView != null) {
+ final BubbleTransitions.BubbleTransition bubbleTransit =
+ removedBubble.getPreparingTransition();
mLayerView.removeBubble(removedBubble, () -> {
+ if (bubbleTransit != null) {
+ bubbleTransit.continueCollapse();
+ }
if (!mBubbleData.hasBubbles() && !isStackExpanded()) {
mLayerView.setVisibility(INVISIBLE);
removeFromWindowManagerMaybe();
@@ -2702,7 +2711,18 @@ public class BubbleController implements ConfigurationChangeListener,
@Override
public void collapseBubbles() {
- mMainExecutor.execute(() -> mController.collapseStack());
+ mMainExecutor.execute(() -> {
+ if (mBubbleData.getSelectedBubble() instanceof Bubble) {
+ if (((Bubble) mBubbleData.getSelectedBubble()).getPreparingTransition()
+ != null) {
+ // Currently preparing a transition which will, itself, collapse the bubble.
+ // For transition preparation, the timing of bubble-collapse must be in
+ // sync with the rest of the set-up.
+ return;
+ }
+ }
+ mController.collapseStack();
+ });
}
@Override
@@ -3094,4 +3114,84 @@ public class BubbleController implements ConfigurationChangeListener,
return mKeyToShownInShadeMap.get(key);
}
}
+
+ private class BubbleTaskViewController implements TaskViewController {
+ private final TaskViewTransitions mBaseTransitions;
+
+ BubbleTaskViewController(TaskViewTransitions baseTransitions) {
+ mBaseTransitions = baseTransitions;
+ }
+
+ @Override
+ public void registerTaskView(TaskViewTaskController tv) {
+ mBaseTransitions.registerTaskView(tv);
+ }
+
+ @Override
+ public void unregisterTaskView(TaskViewTaskController tv) {
+ mBaseTransitions.unregisterTaskView(tv);
+ }
+
+ @Override
+ public void startShortcutActivity(@NonNull TaskViewTaskController destination,
+ @NonNull ShortcutInfo shortcut, @NonNull ActivityOptions options,
+ @Nullable Rect launchBounds) {
+ mBaseTransitions.startShortcutActivity(destination, shortcut, options, launchBounds);
+ }
+
+ @Override
+ public void startActivity(@NonNull TaskViewTaskController destination,
+ @NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+ @NonNull ActivityOptions options, @Nullable Rect launchBounds) {
+ mBaseTransitions.startActivity(destination, pendingIntent, fillInIntent,
+ options, launchBounds);
+ }
+
+ @Override
+ public void startRootTask(@NonNull TaskViewTaskController destination,
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+ @Nullable WindowContainerTransaction wct) {
+ mBaseTransitions.startRootTask(destination, taskInfo, leash, wct);
+ }
+
+ @Override
+ public void removeTaskView(@NonNull TaskViewTaskController taskView,
+ @Nullable WindowContainerToken taskToken) {
+ mBaseTransitions.removeTaskView(taskView, taskToken);
+ }
+
+ @Override
+ public void moveTaskViewToFullscreen(@NonNull TaskViewTaskController taskView) {
+ final TaskInfo tinfo = taskView.getTaskInfo();
+ if (tinfo == null) {
+ return;
+ }
+ Bubble bub = null;
+ for (Bubble b : mBubbleData.getBubbles()) {
+ if (b.getTaskId() == tinfo.taskId) {
+ bub = b;
+ break;
+ }
+ }
+ if (bub == null) {
+ return;
+ }
+ mBubbleTransitions.startConvertFromBubble(bub, tinfo);
+ }
+
+ @Override
+ public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
+ mBaseTransitions.setTaskViewVisible(taskView, visible);
+ }
+
+ @Override
+ public void setTaskBounds(TaskViewTaskController taskView, Rect boundsOnScreen) {
+ mBaseTransitions.setTaskBounds(taskView, boundsOnScreen);
+ }
+
+ @Override
+ public boolean isUsingShellTransitions() {
+ return mBaseTransitions.isUsingShellTransitions();
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 1a61793eab87..a725e04d3f8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -87,6 +87,7 @@ public class BubblePositioner {
private int mExpandedViewLargeScreenWidth;
private int mExpandedViewLargeScreenInsetClosestEdge;
private int mExpandedViewLargeScreenInsetFurthestEdge;
+ private int mExpandedViewBubbleBarWidth;
private int mOverflowWidth;
private int mExpandedViewPadding;
@@ -158,12 +159,13 @@ public class BubblePositioner {
mBubbleOffscreenAmount = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
+ mExpandedViewBubbleBarWidth = Math.min(
+ res.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width),
+ mPositionRect.width() - 2 * mExpandedViewPadding
+ );
if (mShowingInBubbleBar) {
- mExpandedViewLargeScreenWidth = Math.min(
- res.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width),
- mPositionRect.width() - 2 * mExpandedViewPadding
- );
+ mExpandedViewLargeScreenWidth = mExpandedViewBubbleBarWidth;
} else if (mDeviceConfig.isSmallTablet()) {
mExpandedViewLargeScreenWidth = (int) (bounds.width()
* EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT);
@@ -888,7 +890,7 @@ public class BubblePositioner {
* How wide the expanded view should be when showing from the bubble bar.
*/
public int getExpandedViewWidthForBubbleBar(boolean isOverflow) {
- return isOverflow ? mOverflowWidth : mExpandedViewLargeScreenWidth;
+ return isOverflow ? mOverflowWidth : mExpandedViewBubbleBarWidth;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index e37844f53b11..29fb1a23017c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -18,6 +18,8 @@ package com.android.wm.shell.bubbles;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.View.INVISIBLE;
import static android.view.WindowManager.TRANSIT_CHANGE;
import android.annotation.NonNull;
@@ -30,8 +32,10 @@ import android.os.IBinder;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.SurfaceView;
+import android.view.View;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
@@ -53,6 +57,12 @@ import java.util.concurrent.Executor;
public class BubbleTransitions {
private static final String TAG = "BubbleTransitions";
+ /**
+ * Multiplier used to convert a view elevation to an "equivalent" shadow-radius. This is the
+ * same multiple used by skia and surface-outsets in WMS.
+ */
+ private static final float ELEVATION_TO_RADIUS = 2;
+
@NonNull final Transitions mTransitions;
@NonNull final ShellTaskOrganizer mTaskOrganizer;
@NonNull final TaskViewRepository mRepository;
@@ -90,6 +100,44 @@ public class BubbleTransitions {
}
/**
+ * Starts a convert-from-bubble transition.
+ *
+ * @see ConvertFromBubble
+ */
+ public BubbleTransition startConvertFromBubble(Bubble bubble,
+ TaskInfo taskInfo) {
+ ConvertFromBubble convert = new ConvertFromBubble(bubble, taskInfo);
+ return convert;
+ }
+
+ /**
+ * Plucks the task-surface out of an ancestor view while making the view invisible. This helper
+ * attempts to do this seamlessly (ie. view becomes invisible in sync with task reparent).
+ */
+ private void pluck(SurfaceControl taskLeash, View fromView, SurfaceControl dest,
+ float destX, float destY, float cornerRadius, SurfaceControl.Transaction t,
+ Runnable onPlucked) {
+ SurfaceControl.Transaction pluckT = new SurfaceControl.Transaction();
+ pluckT.reparent(taskLeash, dest);
+ t.reparent(taskLeash, dest);
+ pluckT.setPosition(taskLeash, destX, destY);
+ t.setPosition(taskLeash, destX, destY);
+ pluckT.show(taskLeash);
+ pluckT.setAlpha(taskLeash, 1.f);
+ float shadowRadius = fromView.getElevation() * ELEVATION_TO_RADIUS;
+ pluckT.setShadowRadius(taskLeash, shadowRadius);
+ pluckT.setCornerRadius(taskLeash, cornerRadius);
+ t.setShadowRadius(taskLeash, shadowRadius);
+ t.setCornerRadius(taskLeash, cornerRadius);
+
+ // Need to remove the taskview AFTER applying the startTransaction because it isn't
+ // synchronized.
+ pluckT.addTransactionCommittedListener(mMainExecutor, onPlucked::run);
+ fromView.getViewRootImpl().applyTransactionOnDraw(pluckT);
+ fromView.setVisibility(INVISIBLE);
+ }
+
+ /**
* Interface to a bubble-specific transition. Bubble transitions have a multi-step lifecycle
* in order to coordinate with the bubble view logic. These steps are communicated on this
* interface.
@@ -98,6 +146,7 @@ public class BubbleTransitions {
default void surfaceCreated() {}
default void continueExpand() {}
void skip();
+ default void continueCollapse() {}
}
/**
@@ -316,4 +365,154 @@ public class BubbleTransitions {
}
}
}
+
+ /**
+ * BubbleTransition that coordinates the setup for moving a task out of a bubble. The actual
+ * animation is owned by the "receiver" of the task; however, because Bubbles uses TaskView,
+ * we need to do some extra coordination work to get the task surface out of the view
+ * "seamlessly".
+ *
+ * The process here looks like:
+ * 1. Send transition to WM for leaving bubbles mode
+ * 2. in startAnimation, set-up a "pluck" operation to pull the task surface out of taskview
+ * 3. Once "plucked", remove the view (calls continueCollapse when surfaces can be cleaned-up)
+ * 4. Then re-dispatch the transition animation so that the "receiver" can animate it.
+ *
+ * So, constructor -> startAnimation -> continueCollapse -> re-dispatch.
+ */
+ @VisibleForTesting
+ class ConvertFromBubble implements Transitions.TransitionHandler, BubbleTransition {
+ @NonNull final Bubble mBubble;
+ IBinder mTransition;
+ TaskInfo mTaskInfo;
+ SurfaceControl mTaskLeash;
+ SurfaceControl mRootLeash;
+
+ ConvertFromBubble(@NonNull Bubble bubble, TaskInfo taskInfo) {
+ mBubble = bubble;
+ mTaskInfo = taskInfo;
+
+ mBubble.setPreparingTransition(this);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ WindowContainerToken token = mTaskInfo.getToken();
+ wct.setWindowingMode(token, WINDOWING_MODE_UNDEFINED);
+ wct.setAlwaysOnTop(token, false);
+ mTaskOrganizer.setInterceptBackPressedOnTaskRoot(token, false);
+ mTaskViewTransitions.enqueueExternal(
+ mBubble.getTaskView().getController(),
+ () -> {
+ mTransition = mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
+ return mTransition;
+ });
+ }
+
+ @Override
+ public void skip() {
+ mBubble.setPreparingTransition(null);
+ final TaskViewTaskController tv =
+ mBubble.getTaskView().getController();
+ tv.notifyTaskRemovalStarted(tv.getTaskInfo());
+ mTaskLeash = null;
+ }
+
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @android.annotation.Nullable TransitionRequestInfo request) {
+ return null;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ }
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ if (!aborted) return;
+ mTransition = null;
+ skip();
+ mTaskViewTransitions.onExternalDone(transition);
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (mTransition != transition) return false;
+
+ final TaskViewTaskController tv =
+ mBubble.getTaskView().getController();
+ if (tv == null) {
+ mTaskViewTransitions.onExternalDone(transition);
+ return false;
+ }
+
+ TransitionInfo.Change taskChg = null;
+
+ boolean found = false;
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ final TransitionInfo.Change chg = info.getChanges().get(i);
+ if (chg.getTaskInfo() == null) continue;
+ if (chg.getMode() != TRANSIT_CHANGE) continue;
+ if (!mTaskInfo.token.equals(chg.getTaskInfo().token)) continue;
+ found = true;
+ mRepository.remove(tv);
+ taskChg = chg;
+ break;
+ }
+
+ if (!found) {
+ Slog.w(TAG, "Expected a TaskView conversion in this transition but didn't get "
+ + "one, cleaning up the task view");
+ tv.setTaskNotFound();
+ skip();
+ mTaskViewTransitions.onExternalDone(transition);
+ return false;
+ }
+
+ mTaskLeash = taskChg.getLeash();
+ mRootLeash = info.getRoot(0).getLeash();
+
+ SurfaceControl dest =
+ mBubble.getBubbleBarExpandedView().getViewRootImpl().getSurfaceControl();
+ final Runnable onPlucked = () -> {
+ // Need to remove the taskview AFTER applying the startTransaction because
+ // it isn't synchronized.
+ tv.notifyTaskRemovalStarted(tv.getTaskInfo());
+ // Unset after removeView so it can be used to pick a different animation.
+ mBubble.setPreparingTransition(null);
+ mBubbleData.setExpanded(false /* expanded */);
+ };
+ if (dest != null) {
+ pluck(mTaskLeash, mBubble.getBubbleBarExpandedView(), dest,
+ taskChg.getStartAbsBounds().left - info.getRoot(0).getOffset().x,
+ taskChg.getStartAbsBounds().top - info.getRoot(0).getOffset().y,
+ mBubble.getBubbleBarExpandedView().getCornerRadius(), startTransaction,
+ onPlucked);
+ mBubble.getBubbleBarExpandedView().post(() -> mTransitions.dispatchTransition(
+ mTransition, info, startTransaction, finishTransaction, finishCallback,
+ null));
+ } else {
+ onPlucked.run();
+ mTransitions.dispatchTransition(mTransition, info, startTransaction,
+ finishTransaction, finishCallback, null);
+ }
+
+ mTaskViewTransitions.onExternalDone(transition);
+ return true;
+ }
+
+ @Override
+ public void continueCollapse() {
+ mBubble.cleanupTaskView();
+ if (mTaskLeash == null) return;
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.reparent(mTaskLeash, mRootLeash);
+ t.apply();
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 91dcbdf5f117..f3f8d6f96a42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -355,8 +355,10 @@ public class BubbleBarLayerView extends FrameLayout
/** Removes the given {@code bubble}. */
public void removeBubble(Bubble bubble, Runnable endAction) {
+ final boolean inTransition = bubble.getPreparingTransition() != null;
Runnable cleanUp = () -> {
- bubble.cleanupViews();
+ // The transition is already managing the task/wm state.
+ bubble.cleanupViews(!inTransition);
endAction.run();
};
if (mBubbleData.getBubbles().isEmpty()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
index 4cd2fd04d3cf..ff3e65a247ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
@@ -15,16 +15,21 @@
*/
package com.android.wm.shell.common
+import android.annotation.UserIdInt
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.pm.LauncherApps
import android.content.pm.PackageManager
+import android.content.pm.PackageManager.Property
import android.os.UserHandle
import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL
+import com.android.wm.shell.sysui.ShellCommandHandler
+import com.android.wm.shell.sysui.ShellInit
+import java.io.PrintWriter
import java.util.Arrays
/**
@@ -35,12 +40,23 @@ class MultiInstanceHelper @JvmOverloads constructor(
private val packageManager: PackageManager,
private val staticAppsSupportingMultiInstance: Array<String> = context.resources
.getStringArray(R.array.config_appsSupportMultiInstancesSplit),
- private val supportsMultiInstanceProperty: Boolean) {
+ shellInit: ShellInit,
+ private val shellCommandHandler: ShellCommandHandler,
+ private val supportsMultiInstanceProperty: Boolean
+) : ShellCommandHandler.ShellCommandActionHandler {
+
+ init {
+ shellInit.addInitCallback(this::onInit, this)
+ }
+
+ private fun onInit() {
+ shellCommandHandler.addCommandCallback("multi-instance", this, this)
+ }
/**
* Returns whether a specific component desires to be launched in multiple instances.
*/
- fun supportsMultiInstanceSplit(componentName: ComponentName?): Boolean {
+ fun supportsMultiInstanceSplit(componentName: ComponentName?, @UserIdInt userId: Int): Boolean {
if (componentName == null || componentName.packageName == null) {
// TODO(b/262864589): Handle empty component case
return false
@@ -63,8 +79,9 @@ class MultiInstanceHelper @JvmOverloads constructor(
// Check the activity property first
try {
- val activityProp = packageManager.getProperty(
- PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName)
+ val activityProp = packageManager.getPropertyAsUser(
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName.packageName,
+ componentName.className, userId)
// If the above call doesn't throw a NameNotFoundException, then the activity property
// should override the application property value
if (activityProp.isBoolean) {
@@ -80,8 +97,9 @@ class MultiInstanceHelper @JvmOverloads constructor(
// Check the application property otherwise
try {
- val appProp = packageManager.getProperty(
- PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName)
+ val appProp = packageManager.getPropertyAsUser(
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, null /* className */,
+ userId)
if (appProp.isBoolean) {
ProtoLog.v(WM_SHELL, "application=%s supports multi-instance", packageName)
return appProp.boolean
@@ -96,6 +114,66 @@ class MultiInstanceHelper @JvmOverloads constructor(
return false
}
+ override fun onShellCommand(args: Array<out String>?, pw: PrintWriter?): Boolean {
+ if (pw == null || args == null || args.isEmpty()) {
+ return false
+ }
+ when (args[0]) {
+ "list" -> return dumpSupportedApps(pw)
+ }
+ return false
+ }
+
+ override fun printShellCommandHelp(pw: PrintWriter, prefix: String) {
+ pw.println("${prefix}list")
+ pw.println("$prefix Lists all the packages that support the multiinstance property")
+ }
+
+ /**
+ * Dumps the static allowlist and list of apps that have the declared property in the manifest.
+ */
+ private fun dumpSupportedApps(pw: PrintWriter): Boolean {
+ pw.println("Static allow list (for all users):")
+ staticAppsSupportingMultiInstance.forEach { pkg ->
+ pw.println(" $pkg")
+ }
+
+ // TODO(b/391693747): Dump this per-user once PM allows us to query properties
+ // for non-calling users
+ val apps = packageManager.queryApplicationProperty(
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI)
+ val activities = packageManager.queryActivityProperty(
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI)
+ val appsWithProperty = (apps + activities)
+ .sortedWith(object : Comparator<Property?> {
+ override fun compare(o1: Property?, o2: Property?): Int {
+ if (o1?.packageName != o2?.packageName) {
+ return o1?.packageName!!.compareTo(o2?.packageName!!)
+ } else {
+ if (o1?.className != null) {
+ return o1.className!!.compareTo(o2?.className!!)
+ } else if (o2?.className != null) {
+ return -o2.className!!.compareTo(o1?.className!!)
+ }
+ return 0
+ }
+ }
+ })
+ if (appsWithProperty.isNotEmpty()) {
+ pw.println("Apps (User ${context.userId}):")
+ appsWithProperty.forEach { prop ->
+ if (prop.isBoolean && prop.boolean) {
+ if (prop.className != null) {
+ pw.println(" ${prop.packageName}/${prop.className}")
+ } else {
+ pw.println(" ${prop.packageName}")
+ }
+ }
+ }
+ }
+ return true
+ }
+
companion object {
/** Returns the component from a PendingIntent */
@JvmStatic
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index dbf95742ca56..e0a829df79ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -114,6 +114,7 @@ import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.annotations.ShellAnimationThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -258,6 +259,12 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
+ static DesktopModeCompatPolicy provideDesktopModeCompatPolicy(Context context) {
+ return new DesktopModeCompatPolicy(context);
+ }
+
+ @WMSingleton
+ @Provides
static Optional<CompatUIHandler> provideCompatUIController(
Context context,
ShellInit shellInit,
@@ -410,9 +417,13 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
- static MultiInstanceHelper provideMultiInstanceHelper(Context context) {
+ static MultiInstanceHelper provideMultiInstanceHelper(
+ Context context,
+ ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler
+ ) {
return new MultiInstanceHelper(context, context.getPackageManager(),
- Flags.supportsMultiInstanceSystemUi());
+ shellInit, shellCommandHandler, Flags.supportsMultiInstanceSystemUi());
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index c71bf687374d..4bbe22a59315 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -37,6 +37,7 @@ import android.os.UserManager;
import android.view.Choreographer;
import android.view.IWindowManager;
import android.view.WindowManager;
+import android.window.DesktopModeFlags;
import androidx.annotation.OptIn;
@@ -72,7 +73,6 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.UserProfileContexts;
import com.android.wm.shell.common.split.SplitState;
-import com.android.wm.shell.compatui.DesktopModeCompatPolicy;
import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
@@ -133,6 +133,7 @@ import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.annotations.ShellAnimationThread;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -814,7 +815,9 @@ public abstract class WMShellModule {
ReturnToDragStartAnimator returnToDragStartAnimator,
@DynamicOverride DesktopUserRepositories desktopUserRepositories,
DesktopModeEventLogger desktopModeEventLogger,
- WindowDecorTaskResourceLoader windowDecorTaskResourceLoader) {
+ WindowDecorTaskResourceLoader windowDecorTaskResourceLoader,
+ FocusTransitionObserver focusTransitionObserver,
+ @ShellMainThread ShellExecutor mainExecutor) {
return new DesktopTilingDecorViewModel(
context,
mainDispatcher,
@@ -828,7 +831,9 @@ public abstract class WMShellModule {
returnToDragStartAnimator,
desktopUserRepositories,
desktopModeEventLogger,
- windowDecorTaskResourceLoader
+ windowDecorTaskResourceLoader,
+ focusTransitionObserver,
+ mainExecutor
);
}
@@ -929,7 +934,7 @@ public abstract class WMShellModule {
if (DesktopModeStatus.canEnterDesktopMode(context) && useKeyGestureEventHandler()
&& manageKeyGestures()
&& (Flags.enableMoveToNextDisplayShortcut()
- || Flags.enableTaskResizingKeyboardShortcuts())) {
+ || DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue())) {
return Optional.of(new DesktopModeKeyGestureHandler(context,
desktopModeWindowDecorViewModel, desktopTasksController,
inputManager, shellTaskOrganizer, focusTransitionObserver,
@@ -1210,7 +1215,8 @@ public abstract class WMShellModule {
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
IWindowManager windowManager,
Optional<DesktopUserRepositories> desktopUserRepositories,
- Optional<DesktopTasksController> desktopTasksController
+ Optional<DesktopTasksController> desktopTasksController,
+ ShellTaskOrganizer shellTaskOrganizer
) {
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return Optional.empty();
@@ -1224,7 +1230,8 @@ public abstract class WMShellModule {
rootTaskDisplayAreaOrganizer,
windowManager,
desktopUserRepositories.get(),
- desktopTasksController.get()));
+ desktopTasksController.get(),
+ shellTaskOrganizer));
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 793bdf0b5614..413300612f7d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -159,11 +159,12 @@ public abstract class Pip2Module {
PipUiEventLogger pipUiEventLogger,
PipTaskListener pipTaskListener,
@NonNull PipTransitionState pipTransitionState,
+ @NonNull PipDisplayLayoutState pipDisplayLayoutState,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
- systemWindows, pipUiEventLogger, pipTaskListener, pipTransitionState, mainExecutor,
- mainHandler);
+ systemWindows, pipUiEventLogger, pipTaskListener, pipTransitionState,
+ pipDisplayLayoutState, mainExecutor, mainHandler);
}
@@ -178,6 +179,8 @@ public abstract class Pip2Module {
@NonNull PipTransitionState pipTransitionState,
@NonNull PipScheduler pipScheduler,
@NonNull SizeSpecSource sizeSpecSource,
+ @NonNull PipDisplayLayoutState pipDisplayLayoutState,
+ DisplayController displayController,
PipMotionHelper pipMotionHelper,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
@@ -185,8 +188,9 @@ public abstract class Pip2Module {
Optional<PipPerfHintController> pipPerfHintControllerOptional) {
return new PipTouchHandler(context, shellInit, shellCommandHandler, menuPhoneController,
pipBoundsAlgorithm, pipBoundsState, pipTransitionState, pipScheduler,
- sizeSpecSource, pipMotionHelper, floatingContentCoordinator, pipUiEventLogger,
- mainExecutor, pipPerfHintControllerOptional);
+ sizeSpecSource, pipDisplayLayoutState, displayController, pipMotionHelper,
+ floatingContentCoordinator, pipUiEventLogger, mainExecutor,
+ pipPerfHintControllerOptional);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index 760d2124b845..6f455df6cfec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -16,7 +16,10 @@
package com.android.wm.shell.desktopmode
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.app.WindowConfiguration.windowingModeToString
import android.content.Context
import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
@@ -27,6 +30,7 @@ import android.window.WindowContainerTransaction
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
@@ -45,6 +49,7 @@ class DesktopDisplayEventHandler(
private val windowManager: IWindowManager,
private val desktopUserRepositories: DesktopUserRepositories,
private val desktopTasksController: DesktopTasksController,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
) : OnDisplaysChangedListener, OnDeskRemovedListener {
private val desktopRepository: DesktopRepository
@@ -72,6 +77,11 @@ class DesktopDisplayEventHandler(
return
}
logV("Creating new desk in new display#$displayId")
+ // TODO: b/362720497 - when SystemUI crashes with a freeform task open for any reason, the
+ // task is recreated and received in [FreeformTaskListener] before this display callback
+ // is invoked, which results in the repository trying to add the task to a desk before the
+ // desk has been recreated here, which may result in a crash-loop if the repository is
+ // checking that the desk exists before adding a task to it. See b/391984373.
desktopTasksController.createDesk(displayId)
}
@@ -119,13 +129,34 @@ class DesktopDisplayEventHandler(
}
val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)
requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." }
- if (tdaInfo.configuration.windowConfiguration.windowingMode == targetDisplayWindowingMode) {
+ val currentDisplayWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
+ if (currentDisplayWindowingMode == targetDisplayWindowingMode) {
// Already in the target mode.
return
}
+ logV(
+ "As an external display is connected, changing default display's windowing mode from" +
+ " ${windowingModeToString(currentDisplayWindowingMode)}" +
+ " to ${windowingModeToString(targetDisplayWindowingMode)}"
+ )
+
val wct = WindowContainerTransaction()
wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode)
+ shellTaskOrganizer
+ .getRunningTasks(DEFAULT_DISPLAY)
+ .filter { it.activityType == ACTIVITY_TYPE_STANDARD }
+ .forEach {
+ // TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy
+ when (it.windowingMode) {
+ currentDisplayWindowingMode -> {
+ wct.setWindowingMode(it.token, currentDisplayWindowingMode)
+ }
+ targetDisplayWindowingMode -> {
+ wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED)
+ }
+ }
+ }
transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 7897c0aa35bc..f5a95a670036 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -24,10 +24,10 @@ import android.view.MotionEvent
import android.view.MotionEvent.TOOL_TYPE_FINGER
import android.view.MotionEvent.TOOL_TYPE_MOUSE
import android.view.MotionEvent.TOOL_TYPE_STYLUS
+import android.window.DesktopModeFlags
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.protolog.ProtoLog
import com.android.internal.util.FrameworkStatsLog
-import com.android.window.flags.Flags
import com.android.wm.shell.EventLogTags
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -185,7 +185,7 @@ class DesktopModeEventLogger {
displayController: DisplayController? = null,
displayLayoutSize: Size? = null,
) {
- if (!Flags.enableResizingMetrics()) return
+ if (!DesktopModeFlags.ENABLE_RESIZING_METRICS.isTrue) return
val sessionId = currentSessionId.get()
if (sessionId == NO_SESSION_ID) {
@@ -232,7 +232,7 @@ class DesktopModeEventLogger {
displayController: DisplayController? = null,
displayLayoutSize: Size? = null,
) {
- if (!Flags.enableResizingMetrics()) return
+ if (!DesktopModeFlags.ENABLE_RESIZING_METRICS.isTrue) return
val sessionId = currentSessionId.get()
if (sessionId == NO_SESSION_ID) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
index 9334898fdb93..5269318943d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
@@ -23,10 +23,10 @@ import android.hardware.input.InputManager
import android.hardware.input.InputManager.KeyGestureEventHandler
import android.hardware.input.KeyGestureEvent
import android.os.IBinder
+import android.window.DesktopModeFlags
import com.android.hardware.input.Flags.manageKeyGestures
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
-import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
@@ -144,7 +144,8 @@ class DesktopModeKeyGestureHandler(
KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW ->
- enableTaskResizingKeyboardShortcuts() && manageKeyGestures()
+ DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue &&
+ manageKeyGestures()
else -> false
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 6636770895fa..4ff1a5f1be31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -926,7 +926,10 @@ class DesktopRepository(
}
override fun getDesk(deskId: Int): Desk =
- checkNotNull(deskByDisplayId[deskId]) { "Expected desk $deskId to exist" }
+ // TODO: b/362720497 - consider enforcing that the desk has been created before trying
+ // to use it. As of now, there are cases where a task may be created faster than a
+ // desk is, so just create it here if needed. See b/391984373.
+ deskByDisplayId.getOrCreate(deskId)
override fun getActiveDesk(displayId: Int): Desk {
// TODO: 389787966 - consider migrating to an "active" state instead of checking the
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 883c988a79ca..ca71cf303a1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.desktopmode
+import android.annotation.UserIdInt
import android.app.ActivityManager
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
@@ -86,7 +87,6 @@ import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SingleInstanceRemoteListener
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.UserProfileContexts
-import com.android.wm.shell.compatui.DesktopModeCompatPolicy
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -115,6 +115,7 @@ import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_ST
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.annotations.ExternalThread
import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useDesktopOverrideDensity
@@ -516,10 +517,7 @@ class DesktopTasksController(
remoteTransition: RemoteTransition? = null,
callback: IMoveToDesktopCallback? = null,
) {
- if (
- DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue() &&
- desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(task)
- ) {
+ if (desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(task)) {
logW("Cannot enter desktop for taskId %d, ineligible top activity found", task.taskId)
return
}
@@ -1821,8 +1819,7 @@ class DesktopTasksController(
taskRepository.isActiveTask(triggerTask.taskId))
private fun isIncompatibleTask(task: RunningTaskInfo) =
- DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue() &&
- desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(task)
+ desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(task)
private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean =
ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue() &&
@@ -2093,7 +2090,7 @@ class DesktopTasksController(
*/
private fun handleIncompatibleTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
logV("handleIncompatibleTaskLaunch")
- if (!isDesktopModeShowing(task.displayId)) return null
+ if (!isDesktopModeShowing(task.displayId) && !forceEnterDesktop(task.displayId)) return null
// Only update task repository for transparent task.
if (
DesktopModeFlags.INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC
@@ -2745,6 +2742,7 @@ class DesktopTasksController(
// TODO(b/358114479): Move this implementation into a separate class.
override fun onUnhandledDrag(
launchIntent: PendingIntent,
+ @UserIdInt userId: Int,
dragEvent: DragEvent,
onFinishCallback: Consumer<Boolean>,
): Boolean {
@@ -2753,8 +2751,10 @@ class DesktopTasksController(
// Not currently in desktop mode, ignore the drop
return false
}
+
+ // TODO:
val launchComponent = getComponent(launchIntent)
- if (!multiInstanceHelper.supportsMultiInstanceSplit(launchComponent)) {
+ if (!multiInstanceHelper.supportsMultiInstanceSplit(launchComponent, userId)) {
// TODO(b/320797628): Should only return early if there is an existing running task, and
// notify the user as well. But for now, just ignore the drop.
logV("Dropped intent does not support multi-instance")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
index b698040eca73..224ff37a1dca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
@@ -29,7 +29,6 @@ import androidx.core.animation.addListener
import com.android.app.animation.Interpolators
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.compatui.DesktopModeCompatPolicy
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.DesktopWallpaperActivity
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
@@ -37,6 +36,7 @@ import com.android.wm.shell.shared.TransitionUtil.isClosingMode
import com.android.wm.shell.shared.TransitionUtil.isClosingType
import com.android.wm.shell.shared.TransitionUtil.isOpeningMode
import com.android.wm.shell.shared.TransitionUtil.isOpeningType
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionHandler
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index e24b2c5f0134..e8996bc03eeb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -31,6 +31,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
@@ -125,6 +126,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
* drag.
*/
default boolean onUnhandledDrag(@NonNull PendingIntent launchIntent,
+ @UserIdInt int userId,
@NonNull DragEvent dragEvent,
@NonNull Consumer<Boolean> onFinishCallback) {
return false;
@@ -444,8 +446,10 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
return;
}
+ // TODO(b/391624027): Consider piping through launch intent user if needed later
+ final int userId = launchIntent.getCreatorUserHandle().getIdentifier();
final boolean handled = notifyListeners(
- l -> l.onUnhandledDrag(launchIntent, dragEvent, onFinishCallback));
+ l -> l.onUnhandledDrag(launchIntent, userId, dragEvent, onFinishCallback));
if (!handled) {
// Nobody handled this, we still have to notify WM
onFinishCallback.accept(false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
index 44900ce1db8a..65099c2dfb9d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PhonePipMenuController.java
@@ -38,6 +38,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipMediaController.ActionListener;
import com.android.wm.shell.common.pip.PipMenuController;
@@ -121,6 +122,9 @@ public class PhonePipMenuController implements PipMenuController,
@NonNull
private final PipTransitionState mPipTransitionState;
+ @NonNull
+ private final PipDisplayLayoutState mPipDisplayLayoutState;
+
private SurfaceControl mLeash;
private ActionListener mMediaActionListener = new ActionListener() {
@@ -134,7 +138,8 @@ public class PhonePipMenuController implements PipMenuController,
public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
PipMediaController mediaController, SystemWindows systemWindows,
PipUiEventLogger pipUiEventLogger, PipTaskListener pipTaskListener,
- @NonNull PipTransitionState pipTransitionState, ShellExecutor mainExecutor,
+ @NonNull PipTransitionState pipTransitionState,
+ @NonNull PipDisplayLayoutState pipDisplayLayoutState, ShellExecutor mainExecutor,
Handler mainHandler) {
mContext = context;
mPipBoundsState = pipBoundsState;
@@ -142,6 +147,7 @@ public class PhonePipMenuController implements PipMenuController,
mSystemWindows = systemWindows;
mPipTaskListener = pipTaskListener;
mPipTransitionState = pipTransitionState;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
mPipUiEventLogger = pipUiEventLogger;
@@ -218,7 +224,7 @@ public class PhonePipMenuController implements PipMenuController,
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
- 0, SHELL_ROOT_LAYER_PIP);
+ mPipDisplayLayoutState.getDisplayId(), SHELL_ROOT_LAYER_PIP);
setShellRootAccessibilityWindow();
// Make sure the initial actions are set
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index b1984ccef4cb..99c9302edb75 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.pip2.phone;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.view.Display.DEFAULT_DISPLAY;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -219,6 +220,7 @@ public class PipController implements ConfigurationChangeListener,
mPipDisplayLayoutState.setDisplayLayout(layout);
mDisplayController.addDisplayChangingController(this);
+ mDisplayController.addDisplayWindowListener(this);
mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
new ImeListener(mDisplayController, mPipDisplayLayoutState.getDisplayId()) {
@Override
@@ -297,6 +299,22 @@ public class PipController implements ConfigurationChangeListener,
setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
}
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ // If PiP was active on an external display that is removed, clean up states and set
+ // {@link PipDisplayLayoutState} to DEFAULT_DISPLAY.
+ if (Flags.enableConnectedDisplaysPip() && mPipTransitionState.isInPip()
+ && displayId == mPipDisplayLayoutState.getDisplayId()
+ && displayId != DEFAULT_DISPLAY) {
+ mPipTransitionState.setState(PipTransitionState.EXITING_PIP);
+ mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
+
+ mPipDisplayLayoutState.setDisplayId(DEFAULT_DISPLAY);
+ mPipDisplayLayoutState.setDisplayLayout(
+ mDisplayController.getDisplayLayout(DEFAULT_DISPLAY));
+ }
+ }
+
/**
* A callback for any observed transition that contains a display change in its
* {@link android.window.TransitionRequestInfo},
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
index b3070f29c6e2..71697596afd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
@@ -23,6 +23,7 @@ import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
+import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
@@ -34,7 +35,9 @@ import androidx.annotation.NonNull;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.DismissViewUtils;
+import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.shared.bubbles.DismissCircleView;
import com.android.wm.shell.shared.bubbles.DismissView;
@@ -50,6 +53,9 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
/* The multiplier to apply scale the target size by when applying the magnetic field radius */
private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
+ /* The window type to apply to the display */
+ private static final int WINDOW_TYPE = WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+
/**
* MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
* PIP.
@@ -84,16 +90,22 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
private final Context mContext;
private final PipMotionHelper mMotionHelper;
private final PipUiEventLogger mPipUiEventLogger;
- private final WindowManager mWindowManager;
+ private WindowManager mWindowManager;
+ /** The display id for the display that is associated with mWindowManager. */
+ private int mWindowManagerDisplayId = -1;
+ private final PipDisplayLayoutState mPipDisplayLayoutState;
+ private final DisplayController mDisplayController;
private final ShellExecutor mMainExecutor;
public PipDismissTargetHandler(Context context, PipUiEventLogger pipUiEventLogger,
- PipMotionHelper motionHelper, ShellExecutor mainExecutor) {
+ PipMotionHelper motionHelper, PipDisplayLayoutState pipDisplayLayoutState,
+ DisplayController displayController, ShellExecutor mainExecutor) {
mContext = context;
mPipUiEventLogger = pipUiEventLogger;
mMotionHelper = motionHelper;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
+ mDisplayController = displayController;
mMainExecutor = mainExecutor;
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
void init() {
@@ -240,6 +252,8 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
/** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
public void createOrUpdateDismissTarget() {
+ getWindowManager();
+
if (mTargetViewContainer.getParent() == null) {
mTargetViewContainer.cancelAnimators();
@@ -262,7 +276,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
WindowManager.LayoutParams.MATCH_PARENT,
height,
0, windowSize.y - height,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WINDOW_TYPE,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
@@ -308,4 +322,16 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen
mWindowManager.removeViewImmediate(mTargetViewContainer);
}
}
+
+ /** Sets mWindowManager to WindowManager associated with the display where PiP is active on. */
+ private void getWindowManager() {
+ final int pipDisplayId = mPipDisplayLayoutState.getDisplayId();
+ if (mWindowManager != null && pipDisplayId == mWindowManagerDisplayId) {
+ return;
+ }
+ mWindowManagerDisplayId = pipDisplayId;
+ Display display = mDisplayController.getDisplay(mWindowManagerDisplayId);
+ Context uiContext = mContext.createWindowContext(display, WINDOW_TYPE, null);
+ mWindowManager = uiContext.getSystemService(WindowManager.class);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
index ffda56d89276..0a0ecffbea1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.pip2.phone;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
@@ -30,6 +28,7 @@ import android.view.InputEvent;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.io.PrintWriter;
@@ -84,6 +83,7 @@ public class PipInputConsumer {
private final IWindowManager mWindowManager;
private final IBinder mToken;
private final String mName;
+ private final PipDisplayLayoutState mPipDisplayLayoutState;
private final ShellExecutor mMainExecutor;
private InputEventReceiver mInputEventReceiver;
@@ -94,10 +94,11 @@ public class PipInputConsumer {
* @param name the name corresponding to the input consumer that is defined in the system.
*/
public PipInputConsumer(IWindowManager windowManager, String name,
- ShellExecutor mainExecutor) {
+ PipDisplayLayoutState pipDisplayLayoutState, ShellExecutor mainExecutor) {
mWindowManager = windowManager;
mToken = new Binder();
mName = name;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
mMainExecutor = mainExecutor;
}
@@ -138,9 +139,9 @@ public class PipInputConsumer {
}
final InputChannel inputChannel = new InputChannel();
try {
- // TODO(b/113087003): Support Picture-in-picture in multi-display.
- mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
- mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
+ final int displayId = mPipDisplayLayoutState.getDisplayId();
+ mWindowManager.destroyInputConsumer(mToken, displayId);
+ mWindowManager.createInputConsumer(mToken, mName, displayId, inputChannel);
} catch (RemoteException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Failed to create input consumer, %s", TAG, e);
@@ -162,8 +163,7 @@ public class PipInputConsumer {
return;
}
try {
- // TODO(b/113087003): Support Picture-in-picture in multi-display.
- mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
+ mWindowManager.destroyInputConsumer(mToken, mPipDisplayLayoutState.getDisplayId());
} catch (RemoteException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Failed to destroy input consumer, %s", TAG, e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index d98be55f28e1..e4be3f60f86e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -44,6 +44,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipPinchResizingAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
@@ -70,9 +71,9 @@ public class PipResizeGestureHandler implements
private final PipScheduler mPipScheduler;
private final PipTransitionState mPipTransitionState;
private final PhonePipMenuController mPhonePipMenuController;
+ private final PipDisplayLayoutState mPipDisplayLayoutState;
private final PipUiEventLogger mPipUiEventLogger;
private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
- private final int mDisplayId;
private final ShellExecutor mMainExecutor;
private final PointF mDownPoint = new PointF();
@@ -120,10 +121,10 @@ public class PipResizeGestureHandler implements
PipTransitionState pipTransitionState,
PipUiEventLogger pipUiEventLogger,
PhonePipMenuController menuActivityController,
+ PipDisplayLayoutState pipDisplayLayoutState,
ShellExecutor mainExecutor,
@Nullable PipPerfHintController pipPerfHintController) {
mContext = context;
- mDisplayId = context.getDisplayId();
mMainExecutor = mainExecutor;
mPipPerfHintController = pipPerfHintController;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
@@ -135,6 +136,7 @@ public class PipResizeGestureHandler implements
mPipTransitionState.addPipTransitionStateChangedListener(this);
mPhonePipMenuController = menuActivityController;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
mPipUiEventLogger = pipUiEventLogger;
mPinchResizingAlgorithm = new PipPinchResizingAlgorithm();
}
@@ -197,7 +199,7 @@ public class PipResizeGestureHandler implements
if (mIsEnabled) {
// Register input event receiver
mInputMonitor = mContext.getSystemService(InputManager.class).monitorGestureInput(
- "pip-resize", mDisplayId);
+ "pip-resize", mPipDisplayLayoutState.getDisplayId());
try {
mMainExecutor.executeBlocking(() -> {
mInputEventReceiver = new PipResizeInputEventReceiver(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index fc3fbe299605..35cd1a2e681f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -54,10 +54,12 @@ import android.view.accessibility.AccessibilityWindowInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipDoubleTapHelper;
import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
@@ -91,6 +93,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
@NonNull private final PipTransitionState mPipTransitionState;
@NonNull private final PipScheduler mPipScheduler;
@NonNull private final SizeSpecSource mSizeSpecSource;
+ @NonNull private final PipDisplayLayoutState mPipDisplayLayoutState;
private final PipUiEventLogger mPipUiEventLogger;
private final PipDismissTargetHandler mPipDismissTargetHandler;
private final ShellExecutor mMainExecutor;
@@ -183,6 +186,8 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
@NonNull PipTransitionState pipTransitionState,
@NonNull PipScheduler pipScheduler,
@NonNull SizeSpecSource sizeSpecSource,
+ @NonNull PipDisplayLayoutState pipDisplayLayoutState,
+ DisplayController displayController,
PipMotionHelper pipMotionHelper,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
@@ -200,6 +205,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
mPipScheduler = pipScheduler;
mSizeSpecSource = sizeSpecSource;
+ mPipDisplayLayoutState = pipDisplayLayoutState;
mMenuController = menuController;
mPipUiEventLogger = pipUiEventLogger;
mFloatingContentCoordinator = floatingContentCoordinator;
@@ -208,7 +214,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
mMotionHelper = pipMotionHelper;
mPipScheduler.setUpdateMovementBoundsRunnable(this::updateMovementBounds);
mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
- mMotionHelper, mainExecutor);
+ mMotionHelper, mPipDisplayLayoutState, displayController, mainExecutor);
mTouchState = new PipTouchState(ViewConfiguration.get(context),
() -> {
mMenuController.showMenuWithPossibleDelay(MENU_STATE_FULL,
@@ -220,8 +226,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
mainExecutor);
mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm,
pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, pipUiEventLogger,
- menuController, mainExecutor,
- mPipPerfHintController);
+ menuController, mPipDisplayLayoutState, mainExecutor, mPipPerfHintController);
mPipBoundsState.addOnAspectRatioChangedCallback(aspectRatio -> {
updateMinMaxSize(aspectRatio);
onAspectRatioChanged();
@@ -264,7 +269,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
mPipDismissTargetHandler.init();
mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(),
- INPUT_CONSUMER_PIP, mMainExecutor);
+ INPUT_CONSUMER_PIP, mPipDisplayLayoutState, mMainExecutor);
mPipInputConsumer.setInputListener(this::handleTouchEvent);
mPipInputConsumer.setRegistrationListener(this::onRegistrationChanged);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 272cb4372acf..03327bf463e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -70,6 +70,7 @@ import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
import com.android.wm.shell.pip2.animation.PipEnterAnimator;
import com.android.wm.shell.pip2.animation.PipExpandAnimator;
@@ -115,6 +116,7 @@ public class PipTransition extends PipTransitionController implements
private final PipTransitionState mPipTransitionState;
private final PipDisplayLayoutState mPipDisplayLayoutState;
private final DisplayController mDisplayController;
+ private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional;
private final Optional<DesktopWallpaperActivityTokenProvider>
mDesktopWallpaperActivityTokenProviderOptional;
@@ -171,6 +173,7 @@ public class PipTransition extends PipTransitionController implements
mPipTransitionState.addPipTransitionStateChangedListener(this);
mPipDisplayLayoutState = pipDisplayLayoutState;
mDisplayController = displayController;
+ mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(mContext);
mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional;
mDesktopWallpaperActivityTokenProviderOptional =
desktopWallpaperActivityTokenProviderOptional;
@@ -343,6 +346,25 @@ public class PipTransition extends PipTransitionController implements
}
}
+ @Override
+ public boolean syncPipSurfaceState(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ final TransitionInfo.Change pipChange = getPipChange(info);
+ if (pipChange == null) return false;
+
+ // add shadow and corner radii
+ final SurfaceControl leash = pipChange.getLeash();
+ final boolean isInPip = mPipTransitionState.isInPip();
+
+ mPipSurfaceTransactionHelper.round(startTransaction, leash, isInPip)
+ .shadow(startTransaction, leash, isInPip);
+ mPipSurfaceTransactionHelper.round(finishTransaction, leash, isInPip)
+ .shadow(finishTransaction, leash, isInPip);
+
+ return true;
+ }
+
//
// Animation schedulers and entry points
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 99a89a6b884f..ae0159263364 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -649,11 +649,12 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
@Nullable Bundle options, UserHandle user) {
if (options == null) options = new Bundle();
final ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+ final int userId = user.getIdentifier();
if (samePackage(packageName, getPackageName(reverseSplitPosition(position), null),
- user.getIdentifier(), getUserId(reverseSplitPosition(position), null))) {
+ userId, getUserId(reverseSplitPosition(position), null))) {
if (mMultiInstanceHelpher.supportsMultiInstanceSplit(
- getShortcutComponent(packageName, shortcutId, user, mLauncherApps))) {
+ getShortcutComponent(packageName, shortcutId, user, mLauncherApps), userId)) {
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else if (isSplitScreenVisible()) {
@@ -687,7 +688,8 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
final int userId1 = shortcutInfo.getUserId();
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity(),
+ userId1)) {
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
@@ -735,7 +737,8 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
boolean setSecondIntentMultipleTask = false;
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent),
+ userId1)) {
setSecondIntentMultipleTask = true;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
@@ -775,7 +778,8 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
boolean setSecondIntentMultipleTask = false;
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1),
+ userId1)) {
fillInIntent1 = new Intent();
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
setSecondIntentMultipleTask = true;
@@ -858,7 +862,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
return;
}
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(intent))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(intent), userId1)) {
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
// the split and there is no reusable background task.
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index aef75e2dc99e..a0cc2bc8887b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -417,7 +417,8 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
}
}
- void notifyTaskRemovalStarted(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
+ /** Notifies listeners of a task being removed. */
+ public void notifyTaskRemovalStarted(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
if (mListener == null) return;
final int taskId = taskInfo.taskId;
mListenerExecutor.execute(() -> mListener.onTaskRemovalStarted(taskId));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
index 7948eadb28f4..2b2cdf84005c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecorViewModel.java
@@ -16,13 +16,17 @@
package com.android.wm.shell.windowdecor;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
import android.content.Context;
import android.hardware.input.InputManager;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;
import android.view.InputDevice;
+import android.view.InsetsState;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.SurfaceControl;
@@ -33,6 +37,7 @@ import android.window.WindowContainerTransaction;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
@@ -49,7 +54,8 @@ import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSuppl
* Works with decorations that extend {@link CarWindowDecoration}.
*/
public abstract class CarWindowDecorViewModel
- implements WindowDecorViewModel, FocusTransitionListener {
+ implements WindowDecorViewModel, FocusTransitionListener,
+ DisplayInsetsController.OnInsetsChangedListener {
private static final String TAG = "CarWindowDecorViewModel";
private final ShellTaskOrganizer mTaskOrganizer;
@@ -57,31 +63,37 @@ public abstract class CarWindowDecorViewModel
private final @ShellBackgroundThread ShellExecutor mBgExecutor;
private final ShellExecutor mMainExecutor;
private final DisplayController mDisplayController;
+ private final DisplayInsetsController mDisplayInsetsController;
private final FocusTransitionObserver mFocusTransitionObserver;
private final SyncTransactionQueue mSyncQueue;
private final SparseArray<CarWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
private final WindowDecorViewHostSupplier<WindowDecorViewHost> mWindowDecorViewHostSupplier;
+ private final IActivityTaskManager mActivityTaskManager;
public CarWindowDecorViewModel(
Context context,
+ @ShellMainThread ShellExecutor mainExecutor,
@ShellBackgroundThread ShellExecutor bgExecutor,
- @ShellMainThread ShellExecutor shellExecutor,
ShellInit shellInit,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
SyncTransactionQueue syncQueue,
FocusTransitionObserver focusTransitionObserver,
WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) {
mContext = context;
- mMainExecutor = shellExecutor;
+ mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
mTaskOrganizer = taskOrganizer;
mDisplayController = displayController;
+ mDisplayInsetsController = displayInsetsController;
mFocusTransitionObserver = focusTransitionObserver;
mSyncQueue = syncQueue;
mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
+ mActivityTaskManager = ActivityTaskManager.getService();
shellInit.addInitCallback(this::onInit, this);
+ displayInsetsController.addGlobalInsetsChangedListener(this);
}
private void onInit() {
@@ -187,6 +199,26 @@ public abstract class CarWindowDecorViewModel
decoration.close();
}
+ @Override
+ public void insetsChanged(int displayId, InsetsState insetsState) {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ try {
+ mActivityTaskManager.getTasks(/* maxNum= */ Integer.MAX_VALUE,
+ /* filterOnlyVisibleRecents= */ false, /* keepIntentExtra= */ false,
+ displayId)
+ .stream().filter(taskInfo -> taskInfo.isVisible && taskInfo.isRunning)
+ .forEach(taskInfo -> {
+ final CarWindowDecoration decoration = mWindowDecorByTaskId.get(
+ taskInfo.taskId);
+ if (decoration != null) {
+ decoration.relayout(taskInfo, t, t);
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot update decoration on inset change on displayId: " + displayId);
+ }
+ }
+
/**
* @return {@code true} if the task/activity associated with {@code taskInfo} should show
* window decoration.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
index 39437845301e..3182745d813e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
@@ -30,7 +30,6 @@ import android.view.WindowInsets;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -47,6 +46,7 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
private WindowDecorLinearLayout mRootView;
private @ShellBackgroundThread final ShellExecutor mBgExecutor;
private final View.OnClickListener mClickListener;
+ private final RelayoutParams mRelayoutParams = new RelayoutParams();
private final RelayoutResult<WindowDecorLinearLayout> mResult = new RelayoutResult<>();
CarWindowDecoration(
@@ -75,7 +75,8 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
@SuppressLint("MissingPermission")
void relayout(ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
- relayout(taskInfo, startT, finishT, /* isCaptionVisible= */ true);
+ relayout(taskInfo, startT, finishT,
+ /* isCaptionVisible= */ mRelayoutParams.mIsCaptionVisible);
}
@SuppressLint("MissingPermission")
@@ -84,12 +85,9 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
boolean isCaptionVisible) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- RelayoutParams relayoutParams = new RelayoutParams();
+ updateRelayoutParams(mRelayoutParams, taskInfo, isCaptionVisible);
- updateRelayoutParams(relayoutParams, taskInfo,
- mDisplayController.getInsetsState(taskInfo.displayId), isCaptionVisible);
-
- relayout(relayoutParams, startT, finishT, wct, mRootView, mResult);
+ relayout(mRelayoutParams, startT, finishT, wct, mRootView, mResult);
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
@@ -118,7 +116,6 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
private void updateRelayoutParams(
RelayoutParams relayoutParams,
ActivityManager.RunningTaskInfo taskInfo,
- @Nullable InsetsState displayInsetsState,
boolean isCaptionVisible) {
relayoutParams.reset();
relayoutParams.mRunningTaskInfo = taskInfo;
@@ -127,16 +124,19 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
relayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
relayoutParams.mIsCaptionVisible =
isCaptionVisible && mIsStatusBarVisible && !mIsKeyguardVisibleAndOccluded;
- if (displayInsetsState != null) {
- relayoutParams.mCaptionTopPadding = getTopPadding(
- taskInfo.getConfiguration().windowConfiguration.getBounds(),
- displayInsetsState);
- }
+ relayoutParams.mCaptionTopPadding = getTopPadding(taskInfo, relayoutParams);
+
relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
relayoutParams.mApplyStartTransactionOnDraw = true;
}
- private static int getTopPadding(Rect taskBounds, @NonNull InsetsState insetsState) {
+ private int getTopPadding(ActivityManager.RunningTaskInfo taskInfo,
+ RelayoutParams relayoutParams) {
+ Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
+ InsetsState insetsState = mDisplayController.getInsetsState(taskInfo.displayId);
+ if (insetsState == null) {
+ return relayoutParams.mCaptionTopPadding;
+ }
Insets systemDecor = insetsState.calculateInsets(taskBounds,
WindowInsets.Type.systemBars() & ~WindowInsets.Type.captionBar(),
false /* ignoreVisibility */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 46c5048b2147..2cf574158358 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -108,7 +108,6 @@ import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController;
-import com.android.wm.shell.compatui.DesktopModeCompatPolicy;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
@@ -133,6 +132,7 @@ import com.android.wm.shell.recents.RecentsTransitionStateListener;
import com.android.wm.shell.shared.FocusTransitionListener;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
@@ -1657,8 +1657,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
&& mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) {
return false;
}
- if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
- && mDesktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) {
+ if (mDesktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo)) {
return false;
}
if (isPartOfDefaultHomePackage(taskInfo)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index dd7488e5f2f9..4e125d001076 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -1358,7 +1358,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mWebUri = assistContent == null ? null : AppToWebUtils.getSessionWebUri(assistContent);
updateGenericLink();
final boolean supportsMultiInstance = mMultiInstanceHelper
- .supportsMultiInstanceSplit(mTaskInfo.baseActivity)
+ .supportsMultiInstanceSplit(mTaskInfo.baseActivity, mTaskInfo.userId)
&& Flags.enableDesktopWindowingMultiInstanceFeatures();
final boolean shouldShowManageWindowsButton = supportsMultiInstance
&& mMinimumInstancesFound;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
index d72da3a08de5..8747f63e789f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
@@ -29,6 +29,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayChangeController
import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import com.android.wm.shell.desktopmode.DesktopTasksController
@@ -37,6 +38,7 @@ import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
@@ -58,6 +60,8 @@ class DesktopTilingDecorViewModel(
private val desktopUserRepositories: DesktopUserRepositories,
private val desktopModeEventLogger: DesktopModeEventLogger,
private val taskResourceLoader: WindowDecorTaskResourceLoader,
+ private val focusTransitionObserver: FocusTransitionObserver,
+ private val mainExecutor: ShellExecutor,
) : DisplayChangeController.OnDisplayChangingListener {
@VisibleForTesting
var tilingTransitionHandlerByDisplayId = SparseArray<DesktopTilingWindowDecoration>()
@@ -94,6 +98,8 @@ class DesktopTilingDecorViewModel(
returnToDragStartAnimator,
desktopUserRepositories,
desktopModeEventLogger,
+ focusTransitionObserver,
+ mainExecutor,
)
tilingTransitionHandlerByDisplayId.put(displayId, newHandler)
newHandler
@@ -112,9 +118,10 @@ class DesktopTilingDecorViewModel(
}
fun moveTaskToFrontIfTiled(taskInfo: RunningTaskInfo): Boolean {
+ // Always pass focus=true because taskInfo.isFocused is not updated yet.
return tilingTransitionHandlerByDisplayId
.get(taskInfo.displayId)
- ?.moveTiledPairToFront(taskInfo, isTaskFocused = true) ?: false
+ ?.moveTiledPairToFront(taskInfo.taskId, isFocusedOnDisplay = true) ?: false
}
fun onOverviewAnimationStateChange(isRunning: Boolean) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 6f2323347468..666d4bd046dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -35,11 +35,14 @@ import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import com.android.internal.annotations.VisibleForTesting
import com.android.launcher3.icons.BaseIconFactory
+import com.android.window.flags.Flags
+import com.android.wm.shell.shared.FocusTransitionListener
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -49,6 +52,7 @@ import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_MINIMIZE
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
@@ -78,13 +82,16 @@ class DesktopTilingWindowDecoration(
private val returnToDragStartAnimator: ReturnToDragStartAnimator,
private val desktopUserRepositories: DesktopUserRepositories,
private val desktopModeEventLogger: DesktopModeEventLogger,
+ private val focusTransitionObserver: FocusTransitionObserver,
+ @ShellMainThread private val mainExecutor: ShellExecutor,
private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
) :
Transitions.TransitionHandler,
ShellTaskOrganizer.FocusListener,
ShellTaskOrganizer.TaskVanishedListener,
DragEventListener,
- Transitions.TransitionObserver {
+ Transitions.TransitionObserver,
+ FocusTransitionListener {
companion object {
private val TAG: String = DesktopTilingWindowDecoration::class.java.simpleName
private const val TILING_DIVIDER_TAG = "Tiling Divider"
@@ -176,8 +183,13 @@ class DesktopTilingWindowDecoration(
if (!isTilingManagerInitialised) {
desktopTilingDividerWindowManager = initTilingManagerForDisplay(displayId, config)
isTilingManagerInitialised = true
- shellTaskOrganizer.addFocusListener(this)
- isTilingFocused = true
+
+ if (Flags.enableDisplayFocusInShellTransitions()) {
+ focusTransitionObserver.setLocalFocusTransitionListener(this, mainExecutor)
+ } else {
+ shellTaskOrganizer.addFocusListener(this)
+ isTilingFocused = true
+ }
}
leftTaskResizingHelper?.initIfNeeded()
rightTaskResizingHelper?.initIfNeeded()
@@ -474,23 +486,33 @@ class DesktopTilingWindowDecoration(
}
}
- // Only called if [taskInfo] relates to a focused task
- private fun isTilingFocusRemoved(taskInfo: RunningTaskInfo): Boolean {
+ // Only called if [taskId] relates to a focused task
+ private fun isTilingFocusRemoved(taskId: Int): Boolean {
return isTilingFocused &&
- taskInfo.taskId != leftTaskResizingHelper?.taskInfo?.taskId &&
- taskInfo.taskId != rightTaskResizingHelper?.taskInfo?.taskId
+ taskId != leftTaskResizingHelper?.taskInfo?.taskId &&
+ taskId != rightTaskResizingHelper?.taskInfo?.taskId
}
+ // Overriding ShellTaskOrganizer.FocusListener
override fun onFocusTaskChanged(taskInfo: RunningTaskInfo?) {
+ if (Flags.enableDisplayFocusInShellTransitions()) return
if (taskInfo != null) {
- moveTiledPairToFront(taskInfo)
+ moveTiledPairToFront(taskInfo.taskId, taskInfo.isFocused)
}
}
+ // Overriding FocusTransitionListener
+ override fun onFocusedTaskChanged(taskId: Int,
+ isFocusedOnDisplay: Boolean,
+ isFocusedGlobally: Boolean) {
+ if (!Flags.enableDisplayFocusInShellTransitions()) return
+ moveTiledPairToFront(taskId, isFocusedOnDisplay)
+ }
+
// Only called if [taskInfo] relates to a focused task
- private fun isTilingRefocused(taskInfo: RunningTaskInfo): Boolean {
- return taskInfo.taskId == leftTaskResizingHelper?.taskInfo?.taskId ||
- taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId
+ private fun isTilingRefocused(taskId: Int): Boolean {
+ return taskId == leftTaskResizingHelper?.taskInfo?.taskId ||
+ taskId == rightTaskResizingHelper?.taskInfo?.taskId
}
private fun buildTiledTasksMoveToFront(leftOnTop: Boolean): WindowContainerTransaction {
@@ -582,14 +604,13 @@ class DesktopTilingWindowDecoration(
* If specified, [isTaskFocused] will override [RunningTaskInfo.isFocused]. This is to be used
* when called when the task will be focused, but the [taskInfo] hasn't been updated yet.
*/
- fun moveTiledPairToFront(taskInfo: RunningTaskInfo, isTaskFocused: Boolean? = null): Boolean {
+ fun moveTiledPairToFront(taskId: Int, isFocusedOnDisplay: Boolean): Boolean {
if (!isTilingManagerInitialised) return false
- val isFocused = isTaskFocused ?: taskInfo.isFocused
- if (!isFocused) return false
+ if (!isFocusedOnDisplay) return false
// If a task that isn't tiled is being focused, let the generic handler do the work.
- if (isTilingFocusRemoved(taskInfo)) {
+ if (!Flags.enableDisplayFocusInShellTransitions() && isTilingFocusRemoved(taskId)) {
isTilingFocused = false
return false
}
@@ -597,31 +618,29 @@ class DesktopTilingWindowDecoration(
val leftTiledTask = leftTaskResizingHelper ?: return false
val rightTiledTask = rightTaskResizingHelper ?: return false
if (!allTiledTasksVisible()) return false
- val isLeftOnTop = taskInfo.taskId == leftTiledTask.taskInfo.taskId
- if (isTilingRefocused(taskInfo)) {
- val t = transactionSupplier.get()
- isTilingFocused = true
- if (taskInfo.taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
- desktopTilingDividerWindowManager?.onRelativeLeashChanged(
- leftTiledTask.getLeash(),
- t,
- )
- }
- if (taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
- desktopTilingDividerWindowManager?.onRelativeLeashChanged(
- rightTiledTask.getLeash(),
- t,
- )
- }
- transitions.startTransition(
- TRANSIT_TO_FRONT,
- buildTiledTasksMoveToFront(isLeftOnTop),
- null,
- )
- t.apply()
- return true
+ val isLeftOnTop = taskId == leftTiledTask.taskInfo.taskId
+ if (!isTilingRefocused(taskId)) return false
+ val t = transactionSupplier.get()
+ if (!Flags.enableDisplayFocusInShellTransitions()) isTilingFocused = true
+ if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
+ desktopTilingDividerWindowManager?.onRelativeLeashChanged(
+ leftTiledTask.getLeash(),
+ t,
+ )
}
- return false
+ if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
+ desktopTilingDividerWindowManager?.onRelativeLeashChanged(
+ rightTiledTask.getLeash(),
+ t,
+ )
+ }
+ transitions.startTransition(
+ TRANSIT_TO_FRONT,
+ buildTiledTasksMoveToFront(isLeftOnTop),
+ null,
+ )
+ t.apply()
+ return true
}
private fun allTiledTasksVisible(): Boolean {
@@ -706,7 +725,13 @@ class DesktopTilingWindowDecoration(
}
private fun tearDownTiling() {
- if (isTilingManagerInitialised) shellTaskOrganizer.removeFocusListener(this)
+ if (isTilingManagerInitialised) {
+ if (Flags.enableDisplayFocusInShellTransitions()) {
+ focusTransitionObserver.unsetLocalFocusTransitionListener(this)
+ } else {
+ shellTaskOrganizer.removeFocusListener(this)
+ }
+ }
if (leftTaskResizingHelper == null && rightTaskResizingHelper == null) {
shellTaskOrganizer.removeTaskVanishedListener(this)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
index 966aea3088c4..7855698d0151 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
@@ -21,6 +21,7 @@ import android.tools.NavBar
import android.tools.Rotation
import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.traces.parsers.WindowManagerStateHelper
+import android.window.DesktopModeFlags
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -59,7 +60,7 @@ constructor(
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
if (usingKeyboard) {
- Assume.assumeTrue(Flags.enableTaskResizingKeyboardShortcuts())
+ Assume.assumeTrue(DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue)
}
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
index 46c97b0a1397..2f99fbaba078 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAppWindows.kt
@@ -21,6 +21,7 @@ import android.tools.NavBar
import android.tools.Rotation
import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.traces.parsers.WindowManagerStateHelper
+import android.window.DesktopModeFlags
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -62,7 +63,7 @@ constructor(
Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
Assume.assumeTrue(Flags.enableMinimizeButton())
if (usingKeyboard) {
- Assume.assumeTrue(Flags.enableTaskResizingKeyboardShortcuts())
+ Assume.assumeTrue(DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue)
}
tapl.setEnableRotation(true)
tapl.setExpectedRotation(rotation.value)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt
index 068064402ee5..59d15ca4fa6b 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithKeyboardShortcuts.kt
@@ -20,6 +20,7 @@ import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
import android.tools.traces.parsers.WindowManagerStateHelper
+import android.window.DesktopModeFlags
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -59,7 +60,7 @@ abstract class SnapResizeAppWindowWithKeyboardShortcuts(
@Before
fun setup() {
Assume.assumeTrue(Flags.enableDesktopWindowingMode() &&
- Flags.enableTaskResizingKeyboardShortcuts() && tapl.isTablet)
+ DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue && tapl.isTablet)
testApp.enterDesktopMode(wmHelper, device)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
index b4f514acf2dd..9d0ddbc6de12 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -179,4 +179,33 @@ public class BubbleTransitionsTest extends ShellTestCase {
animCb.getValue().run();
assertTrue(finishCalled[0]);
}
+
+ @Test
+ public void testConvertFromBubble() {
+ ActivityManager.RunningTaskInfo taskInfo = setupBubble();
+ final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertFromBubble(
+ mBubble, taskInfo);
+ final BubbleTransitions.ConvertFromBubble cfb = (BubbleTransitions.ConvertFromBubble) bt;
+ verify(mTransitions).startTransition(anyInt(), any(), eq(cfb));
+ verify(mBubble).setPreparingTransition(eq(bt));
+ assertTrue(mTaskViewTransitions.hasPending());
+
+ final TransitionInfo info = new TransitionInfo(TRANSIT_CHANGE, 0);
+ final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
+ mock(SurfaceControl.class));
+ chg.setMode(TRANSIT_CHANGE);
+ chg.setTaskInfo(taskInfo);
+ info.addChange(chg);
+ info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
+ SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ Transitions.TransitionFinishCallback finishCb = wct -> {};
+ cfb.startAnimation(cfb.mTransition, info, startT, finishT, finishCb);
+
+ // Can really only verify that it interfaces with the taskViewTransitions queue.
+ // The actual functioning of this is tightly-coupled with SurfaceFlinger and renderthread
+ // in order to properly synchronize surface manipulation with drawing and thus can't be
+ // directly tested.
+ assertFalse(mTaskViewTransitions.hasPending());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
index bec91e910cf7..6b0c390ac239 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
@@ -33,8 +33,6 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.eq
import org.mockito.kotlin.any
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.doThrow
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -80,17 +78,19 @@ class MultiInstanceHelperTest : ShellTestCase() {
@Test
fun supportsMultiInstanceSplit_inStaticAllowList() {
val allowList = arrayOf(TEST_PACKAGE)
- val helper = MultiInstanceHelper(mContext, context.packageManager, allowList, true)
+ val helper = MultiInstanceHelper(mContext, context.packageManager, allowList,
+ mock(), mock(), true)
val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
- assertEquals(true, helper.supportsMultiInstanceSplit(component))
+ assertEquals(true, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
}
@Test
fun supportsMultiInstanceSplit_notInStaticAllowList() {
val allowList = arrayOf(TEST_PACKAGE)
- val helper = MultiInstanceHelper(mContext, context.packageManager, allowList, true)
+ val helper = MultiInstanceHelper(mContext, context.packageManager, allowList,
+ mock(), mock(), true)
val component = ComponentName(TEST_NOT_ALLOWED_PACKAGE, TEST_ACTIVITY)
- assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ assertEquals(false, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
}
@Test
@@ -99,17 +99,17 @@ class MultiInstanceHelperTest : ShellTestCase() {
val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
val pm = mock<PackageManager>()
val activityProp = PackageManager.Property("", true, "", "")
- whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component)))
+ whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER_ID)))
.thenReturn(activityProp)
val appProp = PackageManager.Property("", false, "", "")
- whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.packageName)))
+ whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName), eq(null), eq(TEST_OTHER_USER_ID)))
.thenReturn(appProp)
- val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray(), mock(), mock(), true)
// Expect activity property to override application property
- assertEquals(true, helper.supportsMultiInstanceSplit(component))
+ assertEquals(true, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
}
@Test
@@ -118,17 +118,17 @@ class MultiInstanceHelperTest : ShellTestCase() {
val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
val pm = mock<PackageManager>()
val activityProp = PackageManager.Property("", false, "", "")
- whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component)))
+ whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER_ID)))
.thenReturn(activityProp)
val appProp = PackageManager.Property("", true, "", "")
- whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.packageName)))
+ whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName), eq(null), eq(TEST_OTHER_USER_ID)))
.thenReturn(appProp)
- val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray(), mock(), mock(), true)
// Expect activity property to override application property
- assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ assertEquals(false, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
}
@Test
@@ -136,17 +136,17 @@ class MultiInstanceHelperTest : ShellTestCase() {
fun supportsMultiInstanceSplit_noActivityPropertyApplicationPropertyTrue() {
val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
val pm = mock<PackageManager>()
- whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component)))
+ whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER_ID)))
.thenThrow(PackageManager.NameNotFoundException())
val appProp = PackageManager.Property("", true, "", "")
- whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.packageName)))
+ whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName), eq(null), eq(TEST_OTHER_USER_ID)))
.thenReturn(appProp)
- val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray(), mock(), mock(), true)
// Expect fall through to app property
- assertEquals(true, helper.supportsMultiInstanceSplit(component))
+ assertEquals(true, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
}
@Test
@@ -154,15 +154,15 @@ class MultiInstanceHelperTest : ShellTestCase() {
fun supportsMultiInstanceSplit_noActivityOrAppProperty() {
val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
val pm = mock<PackageManager>()
- whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component)))
+ whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER_ID)))
.thenThrow(PackageManager.NameNotFoundException())
- whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.packageName)))
+ whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName), eq(null), eq(TEST_OTHER_USER_ID)))
.thenThrow(PackageManager.NameNotFoundException())
- val helper = MultiInstanceHelper(mContext, pm, emptyArray(), true)
- assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray(), mock(), mock(), true)
+ assertEquals(false, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
}
@Test
@@ -171,24 +171,25 @@ class MultiInstanceHelperTest : ShellTestCase() {
val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
val pm = mock<PackageManager>()
val activityProp = PackageManager.Property("", true, "", "")
- whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component)))
+ whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName), eq(component.className), eq(TEST_OTHER_USER_ID)))
.thenReturn(activityProp)
val appProp = PackageManager.Property("", true, "", "")
- whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.packageName)))
+ whenever(pm.getPropertyAsUser(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName), eq(null), eq(TEST_OTHER_USER_ID)))
.thenReturn(appProp)
- val helper = MultiInstanceHelper(mContext, pm, emptyArray(), false)
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray(), mock(), mock(), false)
// Expect we only check the static list and not the property
- assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ assertEquals(false, helper.supportsMultiInstanceSplit(component, TEST_OTHER_USER_ID))
verify(pm, never()).getProperty(any(), any<ComponentName>())
}
companion object {
val TEST_PACKAGE = "com.android.wm.shell.common"
- val TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.common.fake";
- val TEST_ACTIVITY = "TestActivity";
+ val TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.common.fake"
+ val TEST_ACTIVITY = "TestActivity"
val TEST_SHORTCUT_ID = "test_shortcut_1"
+ val TEST_OTHER_USER_ID = 1234
}
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index 5d8d5a716504..fae7363e0676 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -16,8 +16,10 @@
package com.android.wm.shell.desktopmode
+import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.content.ContentResolver
import android.os.Binder
import android.platform.test.annotations.EnableFlags
@@ -37,7 +39,9 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.window.flags.Flags
import com.android.wm.shell.MockToken
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.common.ShellExecutor
@@ -81,12 +85,20 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories
@Mock private lateinit var mockDesktopRepository: DesktopRepository
@Mock private lateinit var mockDesktopTasksController: DesktopTasksController
+ @Mock private lateinit var shellTaskOrganizer: ShellTaskOrganizer
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var shellInit: ShellInit
private lateinit var handler: DesktopDisplayEventHandler
private val onDisplaysChangedListenerCaptor = argumentCaptor<OnDisplaysChangedListener>()
+ private val runningTasks = mutableListOf<RunningTaskInfo>()
+ private val externalDisplayId = 100
+ private val freeformTask =
+ TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build()
+ private val fullscreenTask =
+ TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
+ private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
@Before
fun setUp() {
@@ -99,8 +111,8 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
shellInit = spy(ShellInit(testExecutor))
whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
- val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
- whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+ .thenReturn(defaultTDA)
handler =
DesktopDisplayEventHandler(
context,
@@ -111,7 +123,11 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
mockWindowManager,
mockDesktopUserRepositories,
mockDesktopTasksController,
+ shellTaskOrganizer,
)
+ whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
+ runningTasks.add(freeformTask)
+ runningTasks.add(fullscreenTask)
shellInit.init()
verify(displayController)
.addDisplayWindowListener(onDisplaysChangedListenerCaptor.capture())
@@ -127,9 +143,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
extendedDisplayEnabled: Boolean,
expectTransition: Boolean,
) {
- val externalDisplayId = 100
- val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
- tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode
+ defaultTDA.configuration.windowConfiguration.windowingMode = defaultWindowingMode
whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
val settingsSession =
ExtendedDisplaySettingsSession(
@@ -138,23 +152,17 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
)
settingsSession.use {
- // The external display connected.
- whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
- .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
- onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
- tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- // The external display disconnected.
- whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
- .thenReturn(intArrayOf(DEFAULT_DISPLAY))
- onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
+ connectExternalDisplay()
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ disconnectExternalDisplay()
if (expectTransition) {
val arg = argumentCaptor<WindowContainerTransaction>()
verify(transitions, times(2))
.startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[tda.token.asBinder()]?.windowingMode)
+ assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.secondValue.changes[tda.token.asBinder()]?.windowingMode)
+ assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
.isEqualTo(defaultWindowingMode)
} else {
verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
@@ -227,6 +235,58 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
}
+ @Test
+ fun displayWindowingModeSwitch_existingTasksOnConnected() {
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
+ WINDOWING_MODE_FULLSCREEN
+ }
+
+ ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
+ connectExternalDisplay()
+
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ }
+ }
+
+ @Test
+ fun displayWindowingModeSwitch_existingTasksOnDisconnected() {
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
+ WINDOWING_MODE_FULLSCREEN
+ }
+
+ ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
+ disconnectExternalDisplay()
+
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+ }
+
+ private fun connectExternalDisplay() {
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
+ }
+
+ private fun disconnectExternalDisplay() {
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY))
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
+ }
+
private class ExtendedDisplaySettingsSession(
private val contentResolver: ContentResolver,
private val overrideValue: Int,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 5736793cd6a9..f5c93ee8ffe4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.desktopmode
import android.graphics.Rect
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.SetFlagsRule
@@ -1159,6 +1160,14 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
assertThat(repo.shouldDesktopBeActiveForPip(DEFAULT_DESKTOP_ID)).isFalse()
}
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun addTask_deskDoesNotExists_createsDesk() {
+ repo.addTask(displayId = 999, taskId = 6, isVisible = true)
+
+ assertThat(repo.getActiveTaskIdsInDesk(999)).contains(6)
+ }
+
class TestListener : DesktopRepository.ActiveTasksListener {
var activeChangesOnDefaultDisplay = 0
var activeChangesOnSecondaryDisplay = 0
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 774fd4e510d2..40c0e3610da2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -101,7 +101,6 @@ import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.UserProfileContexts
-import com.android.wm.shell.compatui.DesktopModeCompatPolicy
import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
@@ -130,6 +129,7 @@ import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING
import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
import com.android.wm.shell.shared.split.SplitScreenConstants
@@ -3019,6 +3019,27 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ fun handleRequest_systemUIActivityWithDisplay_returnSwitchToFullscreenWCT_enforcedDesktop() {
+ whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
+ val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
+ tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ // Set task as systemUI package
+ val systemUIPackageName =
+ context.resources.getString(com.android.internal.R.string.config_systemUi)
+ val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
+ val task =
+ createFreeformTask().apply {
+ baseActivity = baseComponent
+ isTopActivityNoDisplay = false
+ }
+
+ assertThat(controller.isDesktopModeShowing(DEFAULT_DISPLAY)).isFalse()
+ val result = controller.handleRequest(Binder(), createTransition(task))
+ assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_backTransition_singleTaskNoToken_noWallpaper_doesNotHandle() {
val task = setUpFreeformTask()
@@ -5155,7 +5176,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
whenever(mockDragEvent.dragSurface).thenReturn(dragSurface)
whenever(mockDragEvent.x).thenReturn(inputCoordinate.x)
whenever(mockDragEvent.y).thenReturn(inputCoordinate.y)
- whenever(multiInstanceHelper.supportsMultiInstanceSplit(anyOrNull())).thenReturn(true)
+ whenever(multiInstanceHelper.supportsMultiInstanceSplit(anyOrNull(), anyInt()))
+ .thenReturn(true)
whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
doReturn(indicatorType)
.whenever(spyController)
@@ -5169,6 +5191,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
spyController.onUnhandledDrag(
mockPendingIntent,
+ context.userId,
mockDragEvent,
mockCallback as Consumer<Boolean>,
)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index b750684ae23c..dfb1b0c8c642 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -26,7 +26,6 @@ import android.view.WindowManager.TRANSIT_OPEN
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.compatui.DesktopModeCompatPolicy
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTaskBuilder
@@ -34,6 +33,7 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSystemModalTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSystemModalTaskBuilder
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.DesktopWallpaperActivity
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/DesktopModeCompatPolicyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
index e894297d5ea2..8c78debdc19f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/DesktopModeCompatPolicyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package com.android.wm.shell.compatui
+package com.android.wm.shell.shared.desktopmode
import android.content.ComponentName
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.internal.R
+import com.android.wm.shell.compatui.CompatUIShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index bb9703fce2e3..7f6b06d46abf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -213,7 +213,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
@Test
public void startIntent_multiInstancesSupported_appendsMultipleTaskFag() {
- doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
+ doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any(), anyInt());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -252,13 +252,13 @@ public class SplitScreenControllerTests extends ShellTestCase {
verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
isNull(), isNull(), eq(SPLIT_INDEX_0));
- verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
+ verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any(), anyInt());
verify(mStageCoordinator, never()).switchSplitPosition(any());
}
@Test
public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() {
- doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
+ doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any(), anyInt());
doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any(), any());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
@@ -276,14 +276,14 @@ public class SplitScreenControllerTests extends ShellTestCase {
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
SPLIT_INDEX_0);
- verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
+ verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any(), anyInt());
verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
isNull(), isNull(), eq(SPLIT_INDEX_0));
}
@Test
public void startIntent_multiInstancesNotSupported_switchesPositionAfterSplitActivated() {
- doReturn(false).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
+ doReturn(false).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any(), anyInt());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 98529b5aa06d..c5c827467c75 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -55,7 +55,6 @@ import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.SyncTransactionQueue
-import com.android.wm.shell.compatui.DesktopModeCompatPolicy
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler
import com.android.wm.shell.desktopmode.DesktopImmersiveController
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
@@ -70,6 +69,7 @@ import com.android.wm.shell.desktopmode.education.AppToWebEducationController
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 9ea5fd6e1abe..87198d14c839 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -280,7 +280,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
mTestableContext = new TestableContext(mContext);
mTestableContext.ensureTestableResources();
mContext.setMockPackageManager(mMockPackageManager);
- when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any()))
+ when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any(), anyInt()))
.thenReturn(false);
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("applicationLabel");
final ActivityInfo activityInfo = createActivityInfo();
@@ -295,7 +295,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
any(), anyInt(), anyInt(), anyInt(), anyInt()))
.thenReturn(mMockHandleMenu);
- when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
+ when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any(), anyInt()))
+ .thenReturn(false);
when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any()))
.thenReturn(mMockAppHeaderViewHolder);
when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
index 997ece6ecadc..2cabb9a33b86 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
@@ -23,6 +23,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import com.android.wm.shell.desktopmode.DesktopUserRepositories
@@ -30,6 +31,7 @@ import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
@@ -67,6 +69,8 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
private val desktopModeWindowDecorationMock: DesktopModeWindowDecoration = mock()
private val desktopTilingDecoration: DesktopTilingWindowDecoration = mock()
private val taskResourceLoader: WindowDecorTaskResourceLoader = mock()
+ private val focusTransitionObserver: FocusTransitionObserver = mock()
+ private val mainExecutor: ShellExecutor = mock()
private lateinit var desktopTilingDecorViewModel: DesktopTilingDecorViewModel
@Before
@@ -86,6 +90,8 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
userRepositories,
desktopModeEventLogger,
taskResourceLoader,
+ focusTransitionObserver,
+ mainExecutor
)
whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
}
@@ -140,7 +146,7 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
desktopTilingDecorViewModel.moveTaskToFrontIfTiled(task1)
verify(desktopTilingDecoration, times(1))
- .moveTiledPairToFront(any(), isTaskFocused = eq(true))
+ .moveTiledPairToFront(any(), isFocusedOnDisplay = eq(true))
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
index 2f15c2e38855..399a51e1ed08 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -33,6 +33,7 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -42,6 +43,7 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
+import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.DragResizeWindowGeometry
@@ -105,6 +107,8 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
private val mainDispatcher: MainCoroutineDispatcher = mock()
private val bgScope: CoroutineScope = mock()
private val taskResourceLoader: WindowDecorTaskResourceLoader = mock()
+ private val focusTransitionObserver: FocusTransitionObserver = mock()
+ private val mainExecutor: ShellExecutor = mock()
private lateinit var tilingDecoration: DesktopTilingWindowDecoration
private val split_divider_width = 10
@@ -129,6 +133,8 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
returnToDragStartAnimator,
userRepositories,
desktopModeEventLogger,
+ focusTransitionObserver,
+ mainExecutor
)
whenever(context.createContextAsUser(any(), any())).thenReturn(context)
whenever(userRepositories.current).thenReturn(desktopRepository)
@@ -242,7 +248,7 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
BOUNDS,
)
- assertThat(tilingDecoration.moveTiledPairToFront(task2)).isFalse()
+ assertThat(tilingDecoration.moveTiledPairToFront(task2.taskId, false)).isFalse()
verify(transitions, never()).startTransition(any(), any(), any())
}
@@ -272,7 +278,7 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
BOUNDS,
)
- assertThat(tilingDecoration.moveTiledPairToFront(task3)).isFalse()
+ assertThat(tilingDecoration.moveTiledPairToFront(task3.taskId, false)).isFalse()
verify(transitions, never()).startTransition(any(), any(), any())
}
@@ -304,7 +310,7 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
)
task1.isFocused = true
- assertThat(tilingDecoration.moveTiledPairToFront(task1, isTaskFocused = true)).isTrue()
+ assertThat(tilingDecoration.moveTiledPairToFront(task1.taskId, isFocusedOnDisplay = true)).isTrue()
verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null))
}
@@ -336,8 +342,8 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
task1.isFocused = true
task3.isFocused = true
- assertThat(tilingDecoration.moveTiledPairToFront(task3)).isFalse()
- assertThat(tilingDecoration.moveTiledPairToFront(task1)).isTrue()
+ assertThat(tilingDecoration.moveTiledPairToFront(task3.taskId, true)).isFalse()
+ assertThat(tilingDecoration.moveTiledPairToFront(task1.taskId, true)).isTrue()
verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null))
}
@@ -367,8 +373,8 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
BOUNDS,
)
- assertThat(tilingDecoration.moveTiledPairToFront(task3, isTaskFocused = true)).isFalse()
- assertThat(tilingDecoration.moveTiledPairToFront(task1, isTaskFocused = true)).isTrue()
+ assertThat(tilingDecoration.moveTiledPairToFront(task3.taskId, isFocusedOnDisplay = true)).isFalse()
+ assertThat(tilingDecoration.moveTiledPairToFront(task1.taskId, isFocusedOnDisplay = true)).isTrue()
verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null))
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index aa3dbda374be..71013f7f4e34 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -22,6 +22,7 @@ import static android.content.Context.DEVICE_ID_DEFAULT;
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.automaticBtDeviceType;
import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
+import static android.media.audio.Flags.cacheGetStreamVolume;
import static android.media.audio.Flags.FLAG_DEPRECATE_STREAM_BT_SCO;
import static android.media.audio.Flags.FLAG_FOCUS_EXCLUSIVE_WITH_RECORDING;
import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
@@ -1241,7 +1242,12 @@ public class AudioManager {
* @hide
**/
public static final String VOLUME_MAX_CACHING_API = "getStreamMaxVolume";
- private static final int VOLUME_MIN_MAX_CACHING_SIZE = 16;
+ /**
+ * API string for caching the volume for each stream
+ * @hide
+ **/
+ public static final String VOLUME_CACHING_API = "getStreamVolume";
+ private static final int VOLUME_CACHING_SIZE = 16;
private final IpcDataCache.QueryHandler<VolumeCacheQuery, Integer> mVolQuery =
new IpcDataCache.QueryHandler<>() {
@@ -1252,6 +1258,7 @@ public class AudioManager {
return switch (query.queryCommand) {
case QUERY_VOL_MIN -> service.getStreamMinVolume(query.stream);
case QUERY_VOL_MAX -> service.getStreamMaxVolume(query.stream);
+ case QUERY_VOL -> service.getStreamVolume(query.stream);
default -> {
Log.w(TAG, "Not a valid volume cache query: " + query);
yield null;
@@ -1265,29 +1272,40 @@ public class AudioManager {
};
private final IpcDataCache<VolumeCacheQuery, Integer> mVolMinCache =
- new IpcDataCache<>(VOLUME_MIN_MAX_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
+ new IpcDataCache<>(VOLUME_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
VOLUME_MIN_CACHING_API, VOLUME_MIN_CACHING_API, mVolQuery);
private final IpcDataCache<VolumeCacheQuery, Integer> mVolMaxCache =
- new IpcDataCache<>(VOLUME_MIN_MAX_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
+ new IpcDataCache<>(VOLUME_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
VOLUME_MAX_CACHING_API, VOLUME_MAX_CACHING_API, mVolQuery);
+ private final IpcDataCache<VolumeCacheQuery, Integer> mVolCache =
+ new IpcDataCache<>(VOLUME_CACHING_SIZE, IpcDataCache.MODULE_SYSTEM,
+ VOLUME_CACHING_API, VOLUME_CACHING_API, mVolQuery);
+
/**
* Used to invalidate the cache for the given API
* @hide
**/
public static void clearVolumeCache(String api) {
- if (cacheGetStreamMinMaxVolume()) {
+ if (cacheGetStreamMinMaxVolume() && (VOLUME_MAX_CACHING_API.equals(api)
+ || VOLUME_MIN_CACHING_API.equals(api))) {
+ IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, api);
+ } else if (cacheGetStreamVolume() && VOLUME_CACHING_API.equals(api)) {
IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, api);
+ } else {
+ Log.w(TAG, "invalid clearVolumeCache for api " + api);
}
}
private static final int QUERY_VOL_MIN = 1;
private static final int QUERY_VOL_MAX = 2;
+ private static final int QUERY_VOL = 3;
/** @hide */
@IntDef(prefix = "QUERY_VOL", value = {
QUERY_VOL_MIN,
- QUERY_VOL_MAX}
+ QUERY_VOL_MAX,
+ QUERY_VOL}
)
@Retention(RetentionPolicy.SOURCE)
private @interface QueryVolCommand {}
@@ -1297,6 +1315,7 @@ public class AudioManager {
return switch (queryCommand) {
case QUERY_VOL_MIN -> "getStreamMinVolume";
case QUERY_VOL_MAX -> "getStreamMaxVolume";
+ case QUERY_VOL -> "getStreamVolume";
default -> "invalid command";
};
}
@@ -1373,6 +1392,9 @@ public class AudioManager {
* @see #setStreamVolume(int, int, int)
*/
public int getStreamVolume(int streamType) {
+ if (cacheGetStreamVolume()) {
+ return mVolCache.query(new VolumeCacheQuery(streamType, QUERY_VOL));
+ }
final IAudioService service = getService();
try {
return service.getStreamVolume(streamType);
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index 8ae72de3214a..e558209420e0 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -385,7 +385,6 @@ public class MediaQualityContract {
public static final String PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED =
"auto_super_resolution_enabled";
-
/**
* @hide
*
@@ -709,12 +708,6 @@ public class MediaQualityContract {
* @hide
*
*/
- public static final String PARAMETER_ACTIVE_PROFILE = "active_profile";
-
- /**
- * @hide
- *
- */
public static final String PARAMETER_PICTURE_QUALITY_EVENT_TYPE =
"picture_quality_event_type";
@@ -977,11 +970,6 @@ public class MediaQualityContract {
/**
* @hide
*/
- public static final String PARAMETER_ACTIVE_PROFILE = "active_profile";
-
- /**
- * @hide
- */
public static final String PARAMETER_SOUND_STYLE = "sound_style";
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
index 017a1029d35c..209734ca4a53 100644
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
+++ b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioManagerTest.java
@@ -67,6 +67,17 @@ public class AudioManagerTest {
private AudioManager mAudioManager;
+ private static final int[] PUBLIC_STREAM_TYPES = {
+ STREAM_VOICE_CALL,
+ STREAM_SYSTEM,
+ STREAM_RING,
+ STREAM_MUSIC,
+ STREAM_ALARM,
+ STREAM_NOTIFICATION,
+ STREAM_DTMF,
+ STREAM_ACCESSIBILITY,
+ };
+
@Rule
public final AudioVolumesTestRule rule = new AudioVolumesTestRule();
@@ -226,18 +237,8 @@ public class AudioManagerTest {
public void getStreamMinMaxVolume_consistentWithAs() throws Exception {
IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
IAudioService service = IAudioService.Stub.asInterface(b);
- final int[] streamTypes = {
- STREAM_VOICE_CALL,
- STREAM_SYSTEM,
- STREAM_RING,
- STREAM_MUSIC,
- STREAM_ALARM,
- STREAM_NOTIFICATION,
- STREAM_DTMF,
- STREAM_ACCESSIBILITY,
- };
-
- for (int streamType : streamTypes) {
+
+ for (int streamType : PUBLIC_STREAM_TYPES) {
assertEquals(service.getStreamMinVolume(streamType),
mAudioManager.getStreamMinVolume(streamType));
assertEquals(service.getStreamMaxVolume(streamType),
@@ -245,6 +246,17 @@ public class AudioManagerTest {
}
}
+ @Test
+ public void getStreamVolume_consistentWithAs() throws Exception {
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ IAudioService service = IAudioService.Stub.asInterface(b);
+
+ for (int streamType : PUBLIC_STREAM_TYPES) {
+ assertEquals(service.getStreamVolume(streamType),
+ mAudioManager.getStreamVolume(streamType));
+ }
+ }
+
//-----------------------------------------------------------------
// Test Volume per Attributes setter/getters
//-----------------------------------------------------------------
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml
index f55b320269a8..ff22b2e7f86f 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled.xml
@@ -20,7 +20,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:filterTouchesWhenObscured="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml
index b663b6ccc5bf..d878ba0d310b 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_extra.xml
@@ -20,7 +20,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:filterTouchesWhenObscured="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml
index 784e6ad6a9f8..8f0a158c55eb 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_filled_large.xml
@@ -20,7 +20,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:filterTouchesWhenObscured="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml
index 8b44a6539801..0c8996063e9f 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline.xml
@@ -20,7 +20,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:filterTouchesWhenObscured="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml
index f8a2d8fbd975..41d8490feeb3 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_extra.xml
@@ -20,7 +20,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:filterTouchesWhenObscured="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml
index 781a5a136164..958552064c98 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_outline_large.xml
@@ -20,7 +20,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:filterTouchesWhenObscured="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml
index 5b568f870ea4..03ca1f0a1033 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal.xml
@@ -20,7 +20,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:filterTouchesWhenObscured="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml
index 1e7a08b714f1..030ee66fef3f 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_extra.xml
@@ -20,7 +20,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:filterTouchesWhenObscured="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml
index 42116be07041..5c16723f7a63 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout-v35/settingslib_expressive_button_tonal_large.xml
@@ -20,7 +20,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:filterTouchesWhenObscured="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml
index 1ff09901ffaf..5405045a013d 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_button_layout.xml
@@ -20,7 +20,8 @@
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:filterTouchesWhenObscured="true">
<Button
android:id="@+id/settingslib_button"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_number_button.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_number_button.xml
index fa13b4125065..b23c5a510745 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_number_button.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_number_button.xml
@@ -29,7 +29,8 @@
android:layout_height="wrap_content"
android:paddingVertical="@dimen/settingslib_expressive_space_small1"
android:paddingHorizontal="@dimen/settingslib_expressive_space_small4"
- android:background="@drawable/settingslib_number_button_background">
+ android:background="@drawable/settingslib_number_button_background"
+ android:filterTouchesWhenObscured="true">
<TextView
android:id="@+id/settingslib_number_count"
android:layout_width="wrap_content"
diff --git a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_section_button.xml b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_section_button.xml
index e7fb572d06dc..66a4c2e25c77 100644
--- a/packages/SettingsLib/ButtonPreference/res/layout/settingslib_section_button.xml
+++ b/packages/SettingsLib/ButtonPreference/res/layout/settingslib_section_button.xml
@@ -21,7 +21,8 @@
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingVertical="@dimen/settingslib_expressive_space_extrasmall4">
+ android:paddingVertical="@dimen/settingslib_expressive_space_extrasmall4"
+ android:filterTouchesWhenObscured="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/settingslib_section_button"
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
index c36ade979d47..d3219c287f4b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
@@ -41,10 +41,23 @@ public class RestrictedDropDownPreference extends DropDownPreference implements
* package. Marks the preference as disabled if so.
* @param settingIdentifier The key identifying the setting
* @param packageName the package to check the settingIdentifier for
+ * @param settingEnabled Whether the setting in question is enabled
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName, boolean settingEnabled) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, settingEnabled);
+ }
+
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * TODO b/390196024: remove this and update all callers to use the "settingEnabled" version
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
*/
public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
@NonNull String packageName) {
- mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, false);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 332042a5c4f9..ec1b2b3e2589 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -107,12 +107,25 @@ public class RestrictedPreference extends TwoTargetPreference implements
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
+ * TODO b/390196024: remove this and update all callers to use the "settingEnabled" version
* @param settingIdentifier The key identifying the setting
* @param packageName the package to check the settingIdentifier for
*/
public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
@NonNull String packageName) {
- mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, false);
+ }
+
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
+ * @param settingEnabled Whether the setting in question is enabled
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName, boolean settingEnabled) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, settingEnabled);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 25628fba1b66..212e43aa4044 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -204,16 +204,33 @@ public class RestrictedPreferenceHelper {
* package. Marks the preference as disabled if so.
* @param settingIdentifier The key identifying the setting
* @param packageName the package to check the settingIdentifier for
+ * @param settingEnabled Whether the setting in question is enabled
*/
public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
- @NonNull String packageName) {
+ @NonNull String packageName, boolean settingEnabled) {
updatePackageDetails(packageName, android.os.Process.INVALID_UID);
+ if (settingEnabled) {
+ setDisabledByEcm(null);
+ return;
+ }
Intent intent = RestrictedLockUtilsInternal.checkIfRequiresEnhancedConfirmation(
mContext, settingIdentifier, packageName);
setDisabledByEcm(intent);
}
/**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * TODO b/390196024: remove this and update all callers to use the "settingEnabled" version
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, false);
+ }
+
+ /**
* @return EnforcedAdmin if we have been passed the restriction in the xml.
*/
public EnforcedAdmin checkRestrictionEnforced() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
index 573869db5073..f8f16a9dd63b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSelectorWithWidgetPreference.java
@@ -134,10 +134,11 @@ public class RestrictedSelectorWithWidgetPreference extends SelectorWithWidgetPr
*
* @param settingIdentifier The key identifying the setting
* @param packageName the package to check the settingIdentifier for
+ * @param settingEnabled Whether the setting in question is enabled
*/
- public void checkEcmRestrictionAndSetDisabled(
- @NonNull String settingIdentifier, @NonNull String packageName) {
- mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName, boolean settingEnabled) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, settingEnabled);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 0aac9a1104e9..a5fa6a854e97 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -220,10 +220,23 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat implement
* package. Marks the preference as disabled if so.
* @param settingIdentifier The key identifying the setting
* @param packageName the package to check the settingIdentifier for
+ * @param settingEnabled Whether the setting in question is enabled
*/
public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
- @NonNull String packageName) {
- mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+ @NonNull String packageName, boolean settingEnabled) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, settingEnabled);
+ }
+
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * TODO b/390196024: remove this and update all callers to use the "settingEnabled" version
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName, false);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 68e9fe703090..a00484ac28ab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1169,4 +1169,38 @@ public class BluetoothUtils {
String metadataValue = getFastPairCustomizedField(bluetoothDevice, TEMP_BOND_TYPE);
return Objects.equals(metadataValue, TEMP_BOND_DEVICE_METADATA_VALUE);
}
+
+ /**
+ * Set temp bond metadata to device
+ *
+ * @param device the BluetoothDevice to be marked as temp bond
+ *
+ * Note: It is a workaround since Bluetooth API is not ready.
+ * Avoid using this method if possible
+ */
+ public static void setTemporaryBondMetadata(@Nullable BluetoothDevice device) {
+ if (device == null) return;
+ if (!Flags.enableTemporaryBondDevicesUi()) {
+ Log.d(TAG, "Skip setTemporaryBondMetadata, flag is disabled");
+ return;
+ }
+ String fastPairCustomizedMeta = getStringMetaData(device,
+ METADATA_FAST_PAIR_CUSTOMIZED_FIELDS);
+ String fullContentWithTag = generateExpressionWithTag(TEMP_BOND_TYPE,
+ TEMP_BOND_DEVICE_METADATA_VALUE);
+ if (TextUtils.isEmpty(fastPairCustomizedMeta)) {
+ fastPairCustomizedMeta = fullContentWithTag;
+ } else {
+ String oldValue = extraTagValue(TEMP_BOND_TYPE, fastPairCustomizedMeta);
+ if (TextUtils.isEmpty(oldValue)) {
+ fastPairCustomizedMeta += fullContentWithTag;
+ } else {
+ fastPairCustomizedMeta =
+ fastPairCustomizedMeta.replace(
+ generateExpressionWithTag(TEMP_BOND_TYPE, oldValue),
+ fullContentWithTag);
+ }
+ }
+ device.setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, fastPairCustomizedMeta.getBytes());
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
index e3d7902f34b2..00973811dbf0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
@@ -84,7 +84,11 @@ public final class ActionDisabledByAdminControllerFactory {
return false;
}
DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
- return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm,
+ final SupervisionManager sm =
+ android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()
+ ? context.getSystemService(SupervisionManager.class)
+ : null;
+ return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm, sm,
BiometricAuthenticator.TYPE_ANY_BIOMETRIC, new UserHandle(UserHandle.myUserId()));
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index cafe19ff9a9b..7c46db96595f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -22,9 +22,11 @@ import static com.android.settingslib.flags.Flags.FLAG_ENABLE_DETERMINING_ADVANC
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -44,6 +46,8 @@ import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.net.Uri;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.Pair;
@@ -109,6 +113,7 @@ public class BluetoothUtilsTest {
+ "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>";
private static final String TEMP_BOND_METADATA =
"<TEMP_BOND_TYPE>" + LE_AUDIO_SHARING_METADATA + "</TEMP_BOND_TYPE>";
+ private static final String FAKE_TEMP_BOND_METADATA = "<TEMP_BOND_TYPE>fake</TEMP_BOND_TYPE>";
private static final String TEST_EXCLUSIVE_MANAGER_PACKAGE = "com.test.manager";
private static final String TEST_EXCLUSIVE_MANAGER_COMPONENT = "com.test.manager/.component";
private static final int TEST_BROADCAST_ID = 25;
@@ -1348,4 +1353,34 @@ public class BluetoothUtilsTest {
assertThat(BluetoothUtils.isTemporaryBondDevice(mBluetoothDevice)).isTrue();
}
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
+ public void setTemporaryBondDevice_flagOff_doNothing() {
+ when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+ .thenReturn(new byte[]{});
+ BluetoothUtils.setTemporaryBondMetadata(mBluetoothDevice);
+ verify(mBluetoothDevice, never()).setMetadata(eq(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS),
+ any());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
+ public void setTemporaryBondDevice_flagOn_setCorrectValue() {
+ when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+ .thenReturn(new byte[]{});
+ BluetoothUtils.setTemporaryBondMetadata(mBluetoothDevice);
+ verify(mBluetoothDevice).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
+ TEMP_BOND_METADATA.getBytes());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
+ public void setTemporaryBondDevice_flagOff_replaceAndSetCorrectValue() {
+ when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+ .thenReturn(FAKE_TEMP_BOND_METADATA.getBytes());
+ BluetoothUtils.setTemporaryBondMetadata(mBluetoothDevice);
+ verify(mBluetoothDevice).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
+ TEMP_BOND_METADATA.getBytes());
+ }
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 7cf83135c237..a92df8026715 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -237,6 +237,13 @@ flag {
}
flag {
+ name: "notification_bundle_ui"
+ namespace: "systemui"
+ description: "Three-level group UI for notification bundles"
+ bug: "367996732"
+}
+
+flag {
name: "notification_background_tint_optimization"
namespace: "systemui"
description: "Re-enable the codepath that removed tinting of notifications when the"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
index 78ae4af258fc..9545bda80b2d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
@@ -1,9 +1,12 @@
package com.android.systemui.animation
-private const val TAG_WGHT = "wght"
-private const val TAG_WDTH = "wdth"
-private const val TAG_OPSZ = "opsz"
-private const val TAG_ROND = "ROND"
+object GSFAxes {
+ const val WEIGHT = "wght"
+ const val WIDTH = "wdth"
+ const val SLANT = "slnt"
+ const val ROUND = "ROND"
+ const val OPTICAL_SIZE = "opsz"
+}
class FontVariationUtils {
private var mWeight = -1
@@ -21,7 +24,7 @@ class FontVariationUtils {
weight: Int = -1,
width: Int = -1,
opticalSize: Int = -1,
- roundness: Int = -1
+ roundness: Int = -1,
): String {
isUpdated = false
if (weight >= 0 && mWeight != weight) {
@@ -43,16 +46,20 @@ class FontVariationUtils {
}
var resultString = ""
if (mWeight >= 0) {
- resultString += "'$TAG_WGHT' $mWeight"
+ resultString += "'${GSFAxes.WEIGHT}' $mWeight"
}
if (mWidth >= 0) {
- resultString += (if (resultString.isBlank()) "" else ", ") + "'$TAG_WDTH' $mWidth"
+ resultString +=
+ (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.WIDTH}' $mWidth"
}
if (mOpticalSize >= 0) {
- resultString += (if (resultString.isBlank()) "" else ", ") + "'$TAG_OPSZ' $mOpticalSize"
+ resultString +=
+ (if (resultString.isBlank()) "" else ", ") +
+ "'${GSFAxes.OPTICAL_SIZE}' $mOpticalSize"
}
if (mRoundness >= 0) {
- resultString += (if (resultString.isBlank()) "" else ", ") + "'$TAG_ROND' $mRoundness"
+ resultString +=
+ (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.ROUND}' $mRoundness"
}
return if (isUpdated) resultString else ""
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index f6ff3268fca4..36029177d4f6 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -17,6 +17,7 @@ import android.content.Context
import android.content.res.Resources
import android.graphics.Typeface
import android.view.LayoutInflater
+import com.android.systemui.animation.GSFAxes
import com.android.systemui.customization.R
import com.android.systemui.log.core.MessageBuffer
import com.android.systemui.plugins.clocks.ClockController
@@ -107,18 +108,18 @@ class DefaultClockProvider(
// TODO(b/364681643): Variations for retargetted DIGITAL_CLOCK_FLEX
val LEGACY_FLEX_LS_VARIATION =
listOf(
- ClockFontAxisSetting("wght", 600f),
- ClockFontAxisSetting("wdth", 100f),
- ClockFontAxisSetting("ROND", 100f),
- ClockFontAxisSetting("slnt", 0f),
+ ClockFontAxisSetting(GSFAxes.WEIGHT, 600f),
+ ClockFontAxisSetting(GSFAxes.WIDTH, 100f),
+ ClockFontAxisSetting(GSFAxes.ROUND, 100f),
+ ClockFontAxisSetting(GSFAxes.SLANT, 0f),
)
val LEGACY_FLEX_AOD_VARIATION =
listOf(
- ClockFontAxisSetting("wght", 74f),
- ClockFontAxisSetting("wdth", 43f),
- ClockFontAxisSetting("ROND", 100f),
- ClockFontAxisSetting("slnt", 0f),
+ ClockFontAxisSetting(GSFAxes.WEIGHT, 74f),
+ ClockFontAxisSetting(GSFAxes.WIDTH, 43f),
+ ClockFontAxisSetting(GSFAxes.ROUND, 100f),
+ ClockFontAxisSetting(GSFAxes.SLANT, 0f),
)
val FLEX_TYPEFACE by lazy {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index b2bc9ef4e363..cc3769e0a568 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shared.clocks
+import com.android.systemui.animation.GSFAxes
import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.AxisType
@@ -122,7 +123,7 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
val FONT_AXES =
listOf(
ClockFontAxis(
- key = "wght",
+ key = GSFAxes.WEIGHT,
type = AxisType.Float,
minValue = 25f,
currentValue = 400f,
@@ -131,7 +132,7 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
description = "Glyph Weight",
),
ClockFontAxis(
- key = "wdth",
+ key = GSFAxes.WIDTH,
type = AxisType.Float,
minValue = 25f,
currentValue = 100f,
@@ -140,7 +141,7 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
description = "Glyph Width",
),
ClockFontAxis(
- key = "ROND",
+ key = GSFAxes.ROUND,
type = AxisType.Boolean,
minValue = 0f,
currentValue = 0f,
@@ -149,7 +150,7 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
description = "Glyph Roundness",
),
ClockFontAxis(
- key = "slnt",
+ key = GSFAxes.SLANT,
type = AxisType.Boolean,
minValue = 0f,
currentValue = 0f,
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 67cbf3082632..e2bbe0fef3c0 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -21,6 +21,7 @@ import android.view.Gravity
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout
+import com.android.systemui.animation.GSFAxes
import com.android.systemui.customization.R
import com.android.systemui.plugins.clocks.AlarmData
import com.android.systemui.plugins.clocks.ClockAnimations
@@ -130,7 +131,7 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:
if (!isLargeClock) {
axes =
axes.map { axis ->
- if (axis.key == "wdth" && axis.value > SMALL_CLOCK_MAX_WDTH) {
+ if (axis.key == GSFAxes.WIDTH && axis.value > SMALL_CLOCK_MAX_WDTH) {
axis.copy(value = SMALL_CLOCK_MAX_WDTH)
} else {
axis
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index c40bb9a5ebea..3eb519186a3e 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -139,13 +139,49 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
digitalClockTextViewMap.forEach { (_, textView) -> textView.refreshText() }
}
+ override fun setVisibility(visibility: Int) {
+ if (visibility != this.visibility) {
+ logger.d({ "setVisibility(${str1 ?: int1})" }) {
+ int1 = visibility
+ str1 =
+ when (visibility) {
+ VISIBLE -> "VISIBLE"
+ INVISIBLE -> "INVISIBLE"
+ GONE -> "GONE"
+ else -> null
+ }
+ }
+ }
+
+ super.setVisibility(visibility)
+ }
+
+ private var loggedAlpha = 1000f
+
+ override fun setAlpha(alpha: Float) {
+ val delta = if (alpha <= 0f || alpha >= 1f) 0.001f else 0.5f
+ if (abs(loggedAlpha - alpha) >= delta) {
+ loggedAlpha = alpha
+ logger.d({ "setAlpha($double1)" }) { double1 = alpha.toDouble() }
+ }
+ super.setAlpha(alpha)
+ }
+
+ private val isDrawn: Boolean
+ get() = (mPrivateFlags and 0x20 /* PFLAG_DRAWN */) > 0
+
override fun invalidate() {
- logger.d("invalidate()")
+ if (isDrawn && visibility == VISIBLE) {
+ logger.d("invalidate()")
+ }
+
super.invalidate()
}
override fun requestLayout() {
- logger.d("requestLayout()")
+ if (!isLayoutRequested()) {
+ logger.d("requestLayout()")
+ }
super.requestLayout()
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 2b0825f39243..fbd5887c5b54 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -34,6 +34,7 @@ import android.view.View.MeasureSpec.EXACTLY
import android.view.animation.Interpolator
import android.widget.TextView
import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.animation.GSFAxes
import com.android.systemui.animation.TextAnimator
import com.android.systemui.customization.R
import com.android.systemui.log.core.Logger
@@ -44,6 +45,7 @@ import com.android.systemui.shared.clocks.DimensionParser
import com.android.systemui.shared.clocks.FontTextStyle
import com.android.systemui.shared.clocks.LogUtil
import java.lang.Thread
+import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -206,7 +208,10 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
}
override fun onDraw(canvas: Canvas) {
- logger.d({ "onDraw(); ls: $str1" }) { str1 = textAnimator.textInterpolator.shapedText }
+ logger.d({ "onDraw(${str1?.replace("\n", "\\n")})" }) {
+ str1 = textAnimator.textInterpolator.shapedText
+ }
+
val translation = getLocalTranslation()
canvas.translate(translation.x.toFloat(), translation.y.toFloat())
digitTranslateAnimator?.let {
@@ -221,8 +226,42 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
canvas.translate(-translation.x.toFloat(), -translation.y.toFloat())
}
+ override fun setVisibility(visibility: Int) {
+ if (visibility != this.visibility) {
+ logger.d({ "setVisibility(${str1 ?: int1})" }) {
+ int1 = visibility
+ str1 =
+ when (visibility) {
+ VISIBLE -> "VISIBLE"
+ INVISIBLE -> "INVISIBLE"
+ GONE -> "GONE"
+ else -> null
+ }
+ }
+ }
+
+ super.setVisibility(visibility)
+ }
+
+ private var loggedAlpha = 1000f
+
+ override fun setAlpha(alpha: Float) {
+ val delta = if (alpha <= 0f || alpha >= 1f) 0.001f else 0.5f
+ if (abs(loggedAlpha - alpha) >= delta) {
+ loggedAlpha = alpha
+ logger.d({ "setAlpha($double1)" }) { double1 = alpha.toDouble() }
+ }
+ super.setAlpha(alpha)
+ }
+
+ private val isDrawn: Boolean
+ get() = (mPrivateFlags and 0x20 /* PFLAG_DRAWN */) > 0
+
override fun invalidate() {
- logger.d("invalidate()")
+ if (isDrawn && visibility == VISIBLE) {
+ logger.d("invalidate()")
+ }
+
super.invalidate()
(parent as? FlexClockView)?.invalidate()
}
@@ -490,22 +529,22 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe
Paint().also { it.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT) }
val AOD_COLOR = Color.WHITE
- val OPTICAL_SIZE_AXIS = ClockFontAxisSetting("opsz", 144f)
+ val OPTICAL_SIZE_AXIS = ClockFontAxisSetting(GSFAxes.OPTICAL_SIZE, 144f)
val DEFAULT_LS_VARIATION =
listOf(
OPTICAL_SIZE_AXIS,
- ClockFontAxisSetting("wght", 400f),
- ClockFontAxisSetting("wdth", 100f),
- ClockFontAxisSetting("ROND", 0f),
- ClockFontAxisSetting("slnt", 0f),
+ ClockFontAxisSetting(GSFAxes.WEIGHT, 400f),
+ ClockFontAxisSetting(GSFAxes.WIDTH, 100f),
+ ClockFontAxisSetting(GSFAxes.ROUND, 0f),
+ ClockFontAxisSetting(GSFAxes.SLANT, 0f),
)
val DEFAULT_AOD_VARIATION =
listOf(
OPTICAL_SIZE_AXIS,
- ClockFontAxisSetting("wght", 200f),
- ClockFontAxisSetting("wdth", 100f),
- ClockFontAxisSetting("ROND", 0f),
- ClockFontAxisSetting("slnt", 0f),
+ ClockFontAxisSetting(GSFAxes.WEIGHT, 200f),
+ ClockFontAxisSetting(GSFAxes.WIDTH, 100f),
+ ClockFontAxisSetting(GSFAxes.ROUND, 0f),
+ ClockFontAxisSetting(GSFAxes.SLANT, 0f),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
index b0f81c012cca..f44769d522eb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
@@ -7,11 +7,6 @@ import junit.framework.Assert
import org.junit.Test
import org.junit.runner.RunWith
-private const val TAG_WGHT = "wght"
-private const val TAG_WDTH = "wdth"
-private const val TAG_OPSZ = "opsz"
-private const val TAG_ROND = "ROND"
-
@RunWith(AndroidJUnit4::class)
@SmallTest
class FontVariationUtilsTest : SysuiTestCase() {
@@ -23,19 +18,22 @@ class FontVariationUtilsTest : SysuiTestCase() {
weight = 100,
width = 100,
opticalSize = -1,
- roundness = 100
+ roundness = 100,
)
- Assert.assertEquals("'$TAG_WGHT' 100, '$TAG_WDTH' 100, '$TAG_ROND' 100", initFvar)
+ Assert.assertEquals(
+ "'${GSFAxes.WEIGHT}' 100, '${GSFAxes.WIDTH}' 100, '${GSFAxes.ROUND}' 100",
+ initFvar,
+ )
val updatedFvar =
fontVariationUtils.updateFontVariation(
weight = 200,
width = 100,
opticalSize = 0,
- roundness = 100
+ roundness = 100,
)
Assert.assertEquals(
- "'$TAG_WGHT' 200, '$TAG_WDTH' 100, '$TAG_OPSZ' 0, '$TAG_ROND' 100",
- updatedFvar
+ "'${GSFAxes.WEIGHT}' 200, '${GSFAxes.WIDTH}' 100, '${GSFAxes.OPTICAL_SIZE}' 0, '${GSFAxes.ROUND}' 100",
+ updatedFvar,
)
}
@@ -46,14 +44,14 @@ class FontVariationUtilsTest : SysuiTestCase() {
weight = 100,
width = 100,
opticalSize = 0,
- roundness = 100
+ roundness = 100,
)
val updatedFvar1 =
fontVariationUtils.updateFontVariation(
weight = 100,
width = 100,
opticalSize = 0,
- roundness = 100
+ roundness = 100,
)
Assert.assertEquals("", updatedFvar1)
val updatedFvar2 = fontVariationUtils.updateFontVariation()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index 83b68fed768e..647603c418ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -17,8 +17,8 @@
package com.android.systemui.navigationbar.views;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_VISIBLE;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
@@ -31,8 +31,9 @@ import static com.android.systemui.assist.AssistManager.INVOCATION_TYPE_HOME_BUT
import static com.android.systemui.navigationbar.views.NavigationBar.NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS;
import static com.android.systemui.navigationbar.views.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_LONGPRESS;
import static com.android.systemui.navigationbar.views.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_IME_SWITCHER_BUTTON_TAP;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_ALT_BACK;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.google.common.truth.Truth.assertThat;
@@ -500,8 +501,9 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_VISIBLE), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_ALT_BACK), eq(true));
}
/**
@@ -514,8 +516,9 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, false /* showImeSwitcher */);
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING), eq(false));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_VISIBLE), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE), eq(false));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_ALT_BACK), eq(true));
}
/**
@@ -531,8 +534,9 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, 0 /* vis */,
BACK_DISPOSITION_DEFAULT, true /* showImeSwitcher */);
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(false));
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING), eq(false));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_VISIBLE), eq(false));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE), eq(false));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_ALT_BACK), eq(false));
}
/**
@@ -545,8 +549,9 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
BACK_DISPOSITION_ADJUST_NOTHING, true /* showImeSwitcher */);
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SHOWING), eq(true));
- verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_VISIBLE), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE), eq(true));
+ verify(mMockSysUiState).setFlag(eq(SYSUI_STATE_IME_ALT_BACK), eq(false));
}
@Test
@@ -567,26 +572,26 @@ public class NavigationBarTest extends SysuiTestCase {
BACK_DISPOSITION_DEFAULT, true);
// Verify IME window state will be updated in default NavBar & external NavBar state reset.
- assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
- | NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN,
+ assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_VISIBLE
+ | NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE,
defaultNavBar.getNavigationIconHints());
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
- assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_VISIBLE) != 0);
assertFalse((externalNavBar.getNavigationIconHints()
- & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0);
+ & NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE) != 0);
externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, 0 /* vis */,
BACK_DISPOSITION_DEFAULT, false);
// Verify IME window state will be updated in external NavBar & default NavBar state reset.
- assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
- | NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN,
+ assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_VISIBLE
+ | NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE,
externalNavBar.getNavigationIconHints());
assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
- assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_VISIBLE) != 0);
assertFalse((defaultNavBar.getNavigationIconHints()
- & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0);
+ & NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE) != 0);
}
@Test
@@ -603,9 +608,9 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
- assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_VISIBLE) != 0);
assertTrue((mNavigationBar.getNavigationIconHints()
- & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0);
+ & NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE) != 0);
// Verify navbar didn't alter and showing back icon when the keyguard is showing without
// requesting IME insets visible.
@@ -613,9 +618,9 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
- assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_VISIBLE) != 0);
assertFalse((mNavigationBar.getNavigationIconHints()
- & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0);
+ & NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE) != 0);
// Verify navbar altered and showing back icon when the keyguard is showing and
// requesting IME insets visible.
@@ -624,9 +629,9 @@ public class NavigationBarTest extends SysuiTestCase {
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
- assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_VISIBLE) != 0);
assertTrue((mNavigationBar.getNavigationIconHints()
- & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0);
+ & NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE) != 0);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index f005375a2ef9..7bb28dbabd5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -49,6 +49,7 @@ import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
+import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.modesDialogViewModel
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.settings.FakeSettings
@@ -146,6 +147,7 @@ class ModesTileTest : SysuiTestCase() {
tileDataInteractor,
mapper,
userActionInteractor,
+ kosmos.modesDialogViewModel,
)
underTest.initialize()
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
index 1d5e09d9b260..e1e60920ab01 100644
--- a/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_background.xml
@@ -26,6 +26,9 @@
<padding
android:left="20dp"
android:right="20dp" />
+ <!-- TODO(b/294830092): Update to the blur surface effect color token when
+ the resource workaround is resolved and
+ notification_shade_blur is enabled. -->
<solid android:color="@androidprv:color/materialColorSurfaceContainerHigh" />
</shape>
</inset>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 449e4bb46027..724a12c12490 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -267,7 +267,7 @@
<dimen name="notification_max_height">358dp</dimen>
<!-- Height of a large promoted ongoing notification in the status bar -->
- <dimen name="notification_max_height_for_promoted_ongoing">218dp</dimen>
+ <dimen name="notification_max_height_for_promoted_ongoing">272dp</dimen>
<!-- Height of a heads up notification in the status bar for legacy custom views -->
<dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index d363e524a9f2..011458859db4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -23,7 +23,7 @@ import android.os.IRemoteCallback;
import android.view.MotionEvent;
import com.android.systemui.shared.recents.ISystemUiProxy;
-// Next ID: 38
+// Next ID: 39
oneway interface IOverviewProxy {
void onActiveNavBarRegionChanges(in Region activeRegion) = 11;
@@ -154,4 +154,9 @@ oneway interface IOverviewProxy {
* Sent when {@link TaskbarDelegate#onDisplayRemoved} is called.
*/
void onDisplayRemoved(int displayId) = 37;
+
+ /**
+ * Sent when {@link TaskbarDelegate#onDisplayRemoveSystemDecorations} is called.
+ */
+ void onDisplayRemoveSystemDecorations(int displayId) = 38;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 8576a6ebac42..a518c57bdd16 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -72,6 +72,25 @@ public class Task {
@ViewDebug.ExportedProperty(category = "recents")
public final int displayId;
+ /**
+ * The component of the first activity in the task, can be considered the "application" of
+ * this task.
+ */
+ @Nullable
+ public ComponentName baseActivity;
+ /**
+ * The number of activities in this task (including running).
+ */
+ public int numActivities;
+ /**
+ * Whether the top activity is to be displayed. See {@link android.R.attr#windowNoDisplay}.
+ */
+ public boolean isTopActivityNoDisplay;
+ /**
+ * Whether fillsParent() is false for every activity in the tasks stack.
+ */
+ public boolean isActivityStackTransparent;
+
// The source component name which started this task
public final ComponentName sourceComponent;
@@ -90,6 +109,10 @@ public class Task {
this.userId = t.userId;
this.lastActiveTime = t.lastActiveTime;
this.displayId = t.displayId;
+ this.baseActivity = t.baseActivity;
+ this.numActivities = t.numActivities;
+ this.isTopActivityNoDisplay = t.isTopActivityNoDisplay;
+ this.isActivityStackTransparent = t.isActivityStackTransparent;
updateHashCode();
}
@@ -106,7 +129,9 @@ public class Task {
}
public TaskKey(int id, int windowingMode, @NonNull Intent intent,
- ComponentName sourceComponent, int userId, long lastActiveTime, int displayId) {
+ ComponentName sourceComponent, int userId, long lastActiveTime, int displayId,
+ @Nullable ComponentName baseActivity, int numActivities,
+ boolean isTopActivityNoDisplay, boolean isActivityStackTransparent) {
this.id = id;
this.windowingMode = windowingMode;
this.baseIntent = intent;
@@ -114,6 +139,10 @@ public class Task {
this.userId = userId;
this.lastActiveTime = lastActiveTime;
this.displayId = displayId;
+ this.baseActivity = baseActivity;
+ this.numActivities = numActivities;
+ this.isTopActivityNoDisplay = isTopActivityNoDisplay;
+ this.isActivityStackTransparent = isActivityStackTransparent;
updateHashCode();
}
@@ -185,6 +214,10 @@ public class Task {
parcel.writeLong(lastActiveTime);
parcel.writeInt(displayId);
parcel.writeTypedObject(sourceComponent, flags);
+ parcel.writeTypedObject(baseActivity, flags);
+ parcel.writeInt(numActivities);
+ parcel.writeBoolean(isTopActivityNoDisplay);
+ parcel.writeBoolean(isActivityStackTransparent);
}
private static TaskKey readFromParcel(Parcel parcel) {
@@ -195,9 +228,14 @@ public class Task {
long lastActiveTime = parcel.readLong();
int displayId = parcel.readInt();
ComponentName sourceComponent = parcel.readTypedObject(ComponentName.CREATOR);
+ ComponentName baseActivity = parcel.readTypedObject(ComponentName.CREATOR);
+ int numActivities = parcel.readInt();
+ boolean isTopActivityNoDisplay = parcel.readBoolean();
+ boolean isActivityStackTransparent = parcel.readBoolean();
return new TaskKey(id, windowingMode, baseIntent, sourceComponent, userId,
- lastActiveTime, displayId);
+ lastActiveTime, displayId, baseActivity, numActivities, isTopActivityNoDisplay,
+ isActivityStackTransparent);
}
@Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 818e39800b0c..32a76fb8439a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -17,8 +17,8 @@
package com.android.systemui.shared.recents.utilities;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_VISIBLE;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE;
import android.annotation.TargetApi;
import android.app.StatusBarManager.NavigationHint;
@@ -107,19 +107,22 @@ public class Utilities {
* Gets the updated navigation icon hints, based on the current ones and the given IME state.
*
* @param oldHints current navigation icon hints.
- * @param backDisposition the IME back disposition mode.
- * @param imeShown whether the IME is currently visible.
- * @param showImeSwitcher whether the IME Switcher button should be shown.
+ * @param backDisposition the IME back disposition mode. Only takes effect if
+ * {@code isImeVisible} is {@code true}.
+ * @param isImeVisible whether the IME is currently visible.
+ * @param showImeSwitcher whether the IME Switcher button should be shown. Only takes effect if
+ * {@code isImeVisible} is {@code true}.
*/
@NavigationHint
public static int calculateNavigationIconHints(@NavigationHint int oldHints,
- @BackDispositionMode int backDisposition, boolean imeShown, boolean showImeSwitcher) {
+ @BackDispositionMode int backDisposition, boolean isImeVisible,
+ boolean showImeSwitcher) {
int hints = oldHints;
switch (backDisposition) {
case InputMethodService.BACK_DISPOSITION_DEFAULT:
case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
- if (imeShown) {
+ if (isImeVisible) {
hints |= NAVIGATION_HINT_BACK_ALT;
} else {
hints &= ~NAVIGATION_HINT_BACK_ALT;
@@ -129,15 +132,15 @@ public class Utilities {
hints &= ~NAVIGATION_HINT_BACK_ALT;
break;
}
- if (imeShown) {
- hints |= NAVIGATION_HINT_IME_SHOWN;
+ if (isImeVisible) {
+ hints |= NAVIGATION_HINT_IME_VISIBLE;
} else {
- hints &= ~NAVIGATION_HINT_IME_SHOWN;
+ hints &= ~NAVIGATION_HINT_IME_VISIBLE;
}
- if (showImeSwitcher) {
- hints |= NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
+ if (showImeSwitcher && isImeVisible) {
+ hints |= NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE;
} else {
- hints &= ~NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
+ hints &= ~NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE;
}
return hints;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index f87d9b05d795..0048fd4d33d3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -99,11 +99,11 @@ public class QuickStepContract {
// Allow system gesture no matter the system bar(s) is visible or not
public static final long SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1L << 17;
// The IME is visible.
- public static final long SYSUI_STATE_IME_SHOWING = 1L << 18;
+ public static final long SYSUI_STATE_IME_VISIBLE = 1L << 18;
// The window magnification is overlapped with system gesture insets at the bottom.
public static final long SYSUI_STATE_MAGNIFICATION_OVERLAP = 1L << 19;
// The IME Switcher button is visible.
- public static final long SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING = 1L << 20;
+ public static final long SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE = 1L << 20;
// Device dozing/AOD state
public static final long SYSUI_STATE_DEVICE_DOZING = 1L << 21;
// The home feature is disabled (either by SUW/SysUI/device policy)
@@ -134,6 +134,8 @@ public class QuickStepContract {
public static final long SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING = 1L << 34;
// Communal hub is showing
public static final long SYSUI_STATE_COMMUNAL_HUB_SHOWING = 1L << 35;
+ // The back button is adjusted for the IME.
+ public static final long SYSUI_STATE_IME_ALT_BACK = 1L << 36;
// Mask for SystemUiStateFlags to isolate SYSUI_STATE_AWAKE and
// SYSUI_STATE_WAKEFULNESS_TRANSITION, to match WAKEFULNESS_* constants
@@ -168,9 +170,9 @@ public class QuickStepContract {
SYSUI_STATE_DIALOG_SHOWING,
SYSUI_STATE_ONE_HANDED_ACTIVE,
SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
- SYSUI_STATE_IME_SHOWING,
+ SYSUI_STATE_IME_VISIBLE,
SYSUI_STATE_MAGNIFICATION_OVERLAP,
- SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING,
+ SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE,
SYSUI_STATE_DEVICE_DOZING,
SYSUI_STATE_BACK_DISABLED,
SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED,
@@ -185,6 +187,7 @@ public class QuickStepContract {
SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED,
SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING,
SYSUI_STATE_COMMUNAL_HUB_SHOWING,
+ SYSUI_STATE_IME_ALT_BACK,
})
public @interface SystemUiStateFlags {}
@@ -244,13 +247,13 @@ public class QuickStepContract {
if ((flags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
str.add("allow_gesture");
}
- if ((flags & SYSUI_STATE_IME_SHOWING) != 0) {
+ if ((flags & SYSUI_STATE_IME_VISIBLE) != 0) {
str.add("ime_visible");
}
if ((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0) {
str.add("magnification_overlap");
}
- if ((flags & SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING) != 0) {
+ if ((flags & SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE) != 0) {
str.add("ime_switcher_button_visible");
}
if ((flags & SYSUI_STATE_DEVICE_DOZING) != 0) {
@@ -295,6 +298,9 @@ public class QuickStepContract {
if ((flags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) {
str.add("communal_hub_showing");
}
+ if ((flags & SYSUI_STATE_IME_ALT_BACK) != 0) {
+ str.add("ime_alt_back");
+ }
return str.toString();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierText.java b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
index ae282c7e14bd..e2ac6a494020 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierText.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
@@ -22,6 +22,7 @@ import android.text.TextUtils;
import android.text.method.SingleLineTransformationMethod;
import android.util.AttributeSet;
import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.TextView;
import com.android.systemui.res.R;
@@ -65,6 +66,14 @@ public class CarrierText extends TextView {
}
}
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ // Clear selected state set by CarrierTextController so "selected" not announced by
+ // accessibility but we can still marquee.
+ info.setSelected(false);
+ }
+
public boolean getShowAirplaneMode() {
return mShowAirplaneMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
index d785b5b5a7e7..464201f6ec12 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/source/MultitaskingShortcutsSource.kt
@@ -29,12 +29,12 @@ import android.view.KeyEvent.KEYCODE_RIGHT_BRACKET
import android.view.KeyEvent.META_CTRL_ON
import android.view.KeyEvent.META_META_ON
import android.view.KeyboardShortcutGroup
+import android.window.DesktopModeFlags
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyboard.shortcut.data.model.shortcutInfo
import com.android.systemui.res.R
import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
-import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import javax.inject.Inject
@@ -85,7 +85,8 @@ constructor(@Main private val resources: Resources, @Application private val con
)
}
if (
- DesktopModeStatus.canEnterDesktopMode(context) && enableTaskResizingKeyboardShortcuts()
+ DesktopModeStatus.canEnterDesktopMode(context) &&
+ DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue
) {
// Snap a freeform window to the left
// - Meta + Left bracket
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
index 550438aa220f..9d43c48ee274 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
@@ -59,7 +59,12 @@ import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.LiveRegionMode
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.liveRegion
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
@@ -244,7 +249,10 @@ private fun ErrorMessageContainer(errorMessage: String) {
lineHeight = 20.sp,
fontWeight = FontWeight.W500,
color = MaterialTheme.colorScheme.error,
- modifier = Modifier.padding(start = 24.dp).width(252.dp),
+ modifier = Modifier.padding(start = 24.dp).width(252.dp).semantics {
+ contentDescription = errorMessage
+ liveRegion = LiveRegionMode.Polite
+ },
)
}
}
@@ -397,6 +405,7 @@ private fun Description(text: String) {
.width(316.dp)
.wrapContentSize(Alignment.Center),
color = MaterialTheme.colorScheme.onSurfaceVariant,
+ textAlign = TextAlign.Center
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
index 425e674ec804..fb69b793d975 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
@@ -21,6 +21,7 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize
import com.android.systemui.log.LogcatEchoTracker
import com.android.systemui.util.time.SystemClock
+import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
@SysUISingleton
@@ -31,7 +32,7 @@ constructor(
private val systemClock: SystemClock,
private val logcatEchoTracker: LogcatEchoTracker,
) {
- private val existingBuffers = mutableMapOf<String, TableLogBuffer>()
+ private val existingBuffers = ConcurrentHashMap<String, TableLogBuffer>()
/**
* Creates a new [TableLogBuffer]. This method should only be called from static contexts, where
@@ -42,17 +43,9 @@ constructor(
* @param maxSize the buffer max size. See [adjustMaxSize]
* @return a new [TableLogBuffer] registered with [DumpManager]
*/
- fun create(
- name: String,
- maxSize: Int,
- ): TableLogBuffer {
+ fun create(name: String, maxSize: Int): TableLogBuffer {
val tableBuffer =
- TableLogBuffer(
- adjustMaxSize(maxSize),
- name,
- systemClock,
- logcatEchoTracker,
- )
+ TableLogBuffer(adjustMaxSize(maxSize), name, systemClock, logcatEchoTracker)
dumpManager.registerTableLogBuffer(name, tableBuffer)
return tableBuffer
}
@@ -66,13 +59,12 @@ constructor(
*
* @return a [TableLogBuffer] suitable for reuse
*/
- fun getOrCreate(
- name: String,
- maxSize: Int,
- ): TableLogBuffer =
- existingBuffers.getOrElse(name) {
- val buffer = create(name, maxSize)
- existingBuffers[name] = buffer
- buffer
+ fun getOrCreate(name: String, maxSize: Int): TableLogBuffer =
+ synchronized(existingBuffers) {
+ existingBuffers.getOrElse(name) {
+ val buffer = create(name, maxSize)
+ existingBuffers[name] = buffer
+ buffer
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 40197b2daf49..0de8c40bddaa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -536,12 +536,12 @@ public final class NavBarHelper implements
}
/**
- * Checks whether the IME is shown on top of the screen, based on the given IME window
+ * Checks whether the IME is visible on top of the screen, based on the given IME window
* visibility flags, and the current keyguard state.
*
* @param vis the IME window visibility.
*/
- public boolean isImeShown(@ImeWindowVisibility int vis) {
+ public boolean isImeVisible(@ImeWindowVisibility int vis) {
View shadeWindowView = mNotificationShadeWindowController.getWindowRootView();
boolean isKeyguardShowing = mKeyguardStateController.isShowing();
boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index ee24b3120966..c2dacd6690a0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -17,8 +17,9 @@
package com.android.systemui.navigationbar;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_VISIBLE;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -30,8 +31,9 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_ALT_BACK;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -263,6 +265,20 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
}
}
+ @Override
+ public void onDisplayRemoveSystemDecorations(int displayId) {
+ CommandQueue.Callbacks.super.onDisplayRemoveSystemDecorations(displayId);
+ if (mOverviewProxyService.getProxy() == null) {
+ return;
+ }
+
+ try {
+ mOverviewProxyService.getProxy().onDisplayRemoveSystemDecorations(displayId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onDisplaySystemDecorationsRemoved() failed", e);
+ }
+ }
+
// Separated into a method to keep setDependencies() clean/readable.
private LightBarTransitionsController createLightBarTransitionsController() {
@@ -362,10 +378,12 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mSysUiState.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
.setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
- .setFlag(SYSUI_STATE_IME_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
- .setFlag(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0)
+ .setFlag(SYSUI_STATE_IME_VISIBLE,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_VISIBLE) != 0)
+ .setFlag(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE) != 0)
+ .setFlag(SYSUI_STATE_IME_ALT_BACK,
+ (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
.setFlag(SYSUI_STATE_HOME_DISABLED,
@@ -485,14 +503,11 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
@Override
public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
@BackDispositionMode int backDisposition, boolean showImeSwitcher) {
- boolean imeShown = mNavBarHelper.isImeShown(vis);
- if (!imeShown) {
- // Count imperceptible changes as visible so we transition taskbar out quickly.
- imeShown = (vis & InputMethodService.IME_VISIBLE_IMPERCEPTIBLE) != 0;
- }
- showImeSwitcher = imeShown && showImeSwitcher;
- int hints = Utilities.calculateNavigationIconHints(mNavigationIconHints, backDisposition,
- imeShown, showImeSwitcher);
+ // Count imperceptible changes as visible so we transition taskbar out quickly.
+ final boolean isImeVisible = mNavBarHelper.isImeVisible(vis)
+ || (vis & InputMethodService.IME_VISIBLE_IMPERCEPTIBLE) != 0;
+ final int hints = Utilities.calculateNavigationIconHints(mNavigationIconHints,
+ backDisposition, isImeVisible, showImeSwitcher);
if (hints == mNavigationIconHints) {
return;
}
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 6842dbc2b12d..2744f9f84ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -17,8 +17,9 @@
package com.android.systemui.navigationbar.views;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_VISIBLE;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
@@ -43,8 +44,9 @@ import static com.android.systemui.shared.statusbar.phone.BarTransitions.Transit
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_ALT_BACK;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_VISIBLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
@@ -652,18 +654,18 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
if (!mEdgeBackGestureHandler.isHandlingGestures()) {
// We're in 2/3 button mode OR back button force-shown in SUW
if (!mImeVisible) {
- // IME not showing, take all touches
+ // IME is not visible, take all touches
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
return;
}
if (!mView.isImeRenderingNavButtons()) {
- // IME showing but not drawing any buttons, take all touches
+ // IME is visible but not drawing any buttons, take all touches
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
return;
}
}
- // When in gestural and the IME is showing, don't use the nearest region since it will
+ // When in gestural and the IME is visible, don't use the nearest region since it will
// take gesture space away from the IME
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
info.touchableRegion.set(
@@ -1138,10 +1140,9 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
if (displayId != mDisplayId) {
return;
}
- boolean imeShown = mNavBarHelper.isImeShown(vis);
- showImeSwitcher = imeShown && showImeSwitcher;
- int hints = Utilities.calculateNavigationIconHints(mNavigationIconHints, backDisposition,
- imeShown, showImeSwitcher);
+ final boolean isImeVisible = mNavBarHelper.isImeVisible(vis);
+ final int hints = Utilities.calculateNavigationIconHints(mNavigationIconHints,
+ backDisposition, isImeVisible, showImeSwitcher);
if (hints == mNavigationIconHints) {
return;
}
@@ -1685,10 +1686,12 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mSysUiFlagsContainer.setFlag(SYSUI_STATE_A11Y_BUTTON_CLICKABLE, clickable)
.setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
- .setFlag(SYSUI_STATE_IME_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
- .setFlag(SYSUI_STATE_IME_SWITCHER_BUTTON_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0)
+ .setFlag(SYSUI_STATE_IME_VISIBLE,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_VISIBLE) != 0)
+ .setFlag(SYSUI_STATE_IME_SWITCHER_BUTTON_VISIBLE,
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE) != 0)
+ .setFlag(SYSUI_STATE_IME_ALT_BACK,
+ (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
.commitUpdate(mDisplayId);
@@ -1953,7 +1956,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
if (newBackAlt != oldBackAlt) {
mView.onBackAltChanged(newBackAlt);
}
- mImeVisible = (hints & NAVIGATION_HINT_IME_SHOWN) != 0;
+ mImeVisible = (hints & NAVIGATION_HINT_IME_VISIBLE) != 0;
mView.setNavigationIconHints(hints);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
index ed8e538cc895..de61d404b1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -17,8 +17,8 @@
package com.android.systemui.navigationbar.views;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_VISIBLE;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE;
import static android.inputmethodservice.InputMethodService.canImeRenderGesturalNavButtons;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
@@ -612,9 +612,9 @@ public class NavigationBarView extends FrameLayout {
updateRecentsIcon();
// Update IME switcher button visibility, a11y and rotate button always overrides
- // the appearance
- boolean isImeSwitcherButtonVisible =
- (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_SHOWN) != 0
+ // the appearance.
+ final boolean isImeSwitcherButtonVisible =
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_BUTTON_VISIBLE) != 0
&& !isImeRenderingNavButtons();
mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, isImeSwitcherButtonVisible);
@@ -670,7 +670,7 @@ public class NavigationBarView extends FrameLayout {
boolean isImeRenderingNavButtons() {
return mImeDrawsImeNavBar
&& mImeCanRenderGesturalNavButtons
- && (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0;
+ && (mNavigationIconHints & NAVIGATION_HINT_IME_VISIBLE) != 0;
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index 0051bf5de7f2..ad5dd27f07c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -35,12 +35,14 @@ import com.android.systemui.modes.shared.ModesUiIcons
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.asQSTileIcon
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.dialog.ModesDetailsViewModel
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
@@ -48,6 +50,7 @@ import com.android.systemui.qs.tiles.impl.modes.ui.ModesTileMapper
import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
import javax.inject.Inject
import kotlinx.coroutines.runBlocking
@@ -67,6 +70,7 @@ constructor(
private val dataInteractor: ModesTileDataInteractor,
private val tileMapper: ModesTileMapper,
private val userActionInteractor: ModesTileUserActionInteractor,
+ private val modesDialogViewModel: ModesDialogViewModel,
) :
QSTileImpl<QSTile.State>(
host,
@@ -114,6 +118,13 @@ constructor(
userActionInteractor.handleToggleClick(model)
}
+ override fun getDetailsViewModel(): TileDetailsViewModel {
+ return ModesDetailsViewModel(
+ onSettingsClick = { userActionInteractor.handleLongClick(null) },
+ viewModel = modesDialogViewModel,
+ )
+ }
+
override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt
new file mode 100644
index 000000000000..511597d05d37
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ModesDetailsViewModel.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.qs.tiles.dialog
+
+import androidx.compose.runtime.Composable
+import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.statusbar.policy.ui.dialog.composable.ModeTileGrid
+import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
+
+/** The view model used for the modes details view in the Quick Settings */
+class ModesDetailsViewModel(
+ private val onSettingsClick: () -> Unit,
+ private val viewModel: ModesDialogViewModel,
+) : TileDetailsViewModel() {
+ @Composable
+ override fun GetContentView() {
+ // TODO(b/378513940): Finish implementing this function.
+ ModeTileGrid(viewModel = viewModel)
+ }
+
+ override fun clickOnSettingsButton() {
+ onSettingsClick()
+ }
+
+ override fun getTitle(): String {
+ // TODO(b/388321032): Replace this string with a string in a translatable xml file.
+ return "Modes"
+ }
+
+ override fun getSubTitle(): String {
+ // TODO(b/388321032): Replace this string with a string in a translatable xml file.
+ return "Silences interruptions from people and apps in different circumstances"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
index 5ce7f0d039c8..b5da044b886a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
@@ -54,7 +54,7 @@ constructor(
handleToggleClick(input.data)
}
is QSTileUserAction.LongClick -> {
- qsTileIntentUserInputHandler.handle(action.expandable, longClickIntent)
+ handleLongClick(action.expandable)
}
}
}
@@ -95,6 +95,10 @@ constructor(
}
}
+ fun handleLongClick(expandable: Expandable?) {
+ qsTileIntentUserInputHandler.handle(expandable, longClickIntent)
+ }
+
companion object {
const val TAG = "ModesTileUserActionInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index d401283aa84e..96192b1ea315 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.footer.ui.view;
import static android.graphics.PorterDuff.Mode.SRC_ATOP;
import static com.android.systemui.Flags.notificationFooterBackgroundTintOptimization;
+import static com.android.systemui.Flags.notificationShadeBlur;
import static com.android.systemui.util.ColorUtilKt.hexColorString;
import android.annotation.ColorInt;
@@ -29,6 +30,7 @@ import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
@@ -39,6 +41,8 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
+import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.common.shared.colors.SurfaceEffectColors;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
@@ -383,9 +387,23 @@ public class FooterView extends StackScrollerDecorView {
final Drawable historyBg = NotifRedesignFooter.isEnabled()
? theme.getDrawable(R.drawable.notif_footer_btn_background) : null;
final @ColorInt int scHigh;
+
if (!notificationFooterBackgroundTintOptimization()) {
- scHigh = mContext.getColor(
- com.android.internal.R.color.materialColorSurfaceContainerHigh);
+ if (notificationShadeBlur()) {
+ Color backgroundColor = Color.valueOf(
+ SurfaceEffectColors.surfaceEffect0(getResources()));
+ scHigh = ColorUtils.setAlphaComponent(backgroundColor.toArgb(), 0xFF);
+ // Apply alpha on background drawables.
+ int backgroundAlpha = (int) (backgroundColor.alpha() * 0xFF);
+ clearAllBg.setAlpha(backgroundAlpha);
+ settingsBg.setAlpha(backgroundAlpha);
+ if (historyBg != null) {
+ historyBg.setAlpha(backgroundAlpha);
+ }
+ } else {
+ scHigh = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainerHigh);
+ }
if (scHigh != 0) {
final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
clearAllBg.setColorFilter(bgColorFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 69b7e892a380..9795cda97f37 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -49,8 +49,10 @@ import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.android.systemui.inputdevice.tutorial.ui.composable.DoneButton
+import com.android.systemui.keyboard.shortcut.ui.composable.hasCompactWindowSize
import com.android.systemui.res.R
import com.android.systemui.touchpad.tutorial.ui.gesture.isFourFingerTouchpadSwipe
import com.android.systemui.touchpad.tutorial.ui.gesture.isThreeFingerTouchpadSwipe
@@ -80,6 +82,7 @@ fun TutorialSelectionScreen(
}
),
) {
+ val padding = if (hasCompactWindowSize()) 24.dp else 60.dp
val configuration = LocalConfiguration.current
when (configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
@@ -88,7 +91,7 @@ fun TutorialSelectionScreen(
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).padding(60.dp),
+ modifier = Modifier.weight(1f).padding(padding),
lastSelectedScreen,
)
}
@@ -98,7 +101,7 @@ fun TutorialSelectionScreen(
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).padding(60.dp),
+ modifier = Modifier.weight(1f).padding(padding),
lastSelectedScreen,
)
}
@@ -106,7 +109,7 @@ fun TutorialSelectionScreen(
// because other composables have weight 1, Done button will be positioned first
DoneButton(
onDoneButtonClicked = onDoneButtonClicked,
- modifier = Modifier.padding(horizontal = 60.dp),
+ modifier = Modifier.padding(horizontal = padding),
)
}
}
@@ -146,7 +149,7 @@ private fun VerticalSelectionButtons(
lastSelectedScreen: Screen,
) {
Column(
- verticalArrangement = Arrangement.spacedBy(20.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier,
) {
@@ -244,8 +247,13 @@ private fun TutorialButton(
modifier = Modifier.width(30.dp).height(30.dp),
tint = iconColor,
)
- Spacer(modifier = Modifier.height(16.dp))
- Text(text = text, style = MaterialTheme.typography.headlineLarge, color = iconColor)
+ if (!hasCompactWindowSize()) Spacer(modifier = Modifier.height(16.dp))
+ Text(
+ text = text,
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.headlineLarge,
+ color = iconColor,
+ )
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 75c629b77700..fda57d6bb986 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -45,6 +45,7 @@ import android.view.MotionEvent.PointerProperties;
import android.view.accessibility.AccessibilityEvent;
import com.android.server.LocalServices;
+import com.android.server.accessibility.autoclick.AutoclickController;
import com.android.server.accessibility.gestures.TouchExplorer;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 3415300ec8d1..1bc9c783df76 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.server.accessibility;
+package com.android.server.accessibility.autoclick;
import static android.view.MotionEvent.BUTTON_PRIMARY;
import static android.view.accessibility.AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
import static android.view.accessibility.AccessibilityManager.AUTOCLICK_DELAY_DEFAULT;
import static android.view.accessibility.AccessibilityManager.AUTOCLICK_IGNORE_MINOR_CURSOR_MOVEMENT_DEFAULT;
-import static com.android.server.accessibility.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
+import static com.android.server.accessibility.autoclick.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
import android.accessibilityservice.AccessibilityTrace;
import android.annotation.NonNull;
@@ -43,6 +43,9 @@ import android.view.WindowManager;
import androidx.annotation.VisibleForTesting;
import com.android.internal.accessibility.util.AccessibilityUtils;
+import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.accessibility.BaseEventStreamTransformation;
+import com.android.server.accessibility.Flags;
/**
* Implements "Automatically click on mouse stop" feature.
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
index 93ce1f044f0b..557e1b2afcd5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.accessibility;
+package com.android.server.accessibility.autoclick;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.accessibility.AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
index c35f4fca6edd..fece7a899f0a 100644
--- a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
+++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
@@ -69,6 +69,8 @@ class AudioManagerShellCommand extends ShellCommand {
return getMinVolume();
case "get-max-volume":
return getMaxVolume();
+ case "get-stream-volume":
+ return getStreamVolume();
case "set-device-volume":
return setDeviceVolume();
case "adj-mute":
@@ -114,6 +116,8 @@ class AudioManagerShellCommand extends ShellCommand {
pw.println(" Gets the min volume for STREAM_TYPE");
pw.println(" get-max-volume STREAM_TYPE");
pw.println(" Gets the max volume for STREAM_TYPE");
+ pw.println(" get-stream-volume STREAM_TYPE");
+ pw.println(" Gets the volume for STREAM_TYPE");
pw.println(" set-device-volume STREAM_TYPE VOLUME_INDEX NATIVE_DEVICE_TYPE");
pw.println(" Sets for NATIVE_DEVICE_TYPE the STREAM_TYPE volume to VOLUME_INDEX");
pw.println(" adj-mute STREAM_TYPE");
@@ -322,6 +326,15 @@ class AudioManagerShellCommand extends ShellCommand {
return 0;
}
+ private int getStreamVolume() {
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ final int stream = readIntArg();
+ final int result = am.getStreamVolume(stream);
+ getOutPrintWriter().println("AudioManager.getStreamVolume(" + stream + ") -> " + result);
+ return 0;
+ }
+
private int setDeviceVolume() {
final Context context = mService.mContext;
final AudioDeviceVolumeManager advm = (AudioDeviceVolumeManager) context.getSystemService(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 336243f0289e..320dd8f188c0 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -50,6 +50,7 @@ import static android.media.AudioManager.STREAM_SYSTEM;
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.automaticBtDeviceType;
import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
+import static android.media.audio.Flags.cacheGetStreamVolume;
import static android.media.audio.Flags.concurrentAudioRecordBypassPermission;
import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency;
import static android.media.audio.Flags.focusFreezeTestApi;
@@ -1910,6 +1911,12 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.onRoutingUpdated();
}
checkMuteAwaitConnection();
+ if (cacheGetStreamVolume()) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "Clear volume cache after routing update");
+ }
+ AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API);
+ }
}
//-----------------------------------------------------------------
@@ -4988,6 +4995,8 @@ public class AudioService extends IAudioService.Stub
+ concurrentAudioRecordBypassPermission());
pw.println("\tandroid.media.audio.Flags.cacheGetStreamMinMaxVolume:"
+ cacheGetStreamMinMaxVolume());
+ pw.println("\tandroid.media.audio.Flags.cacheGetStreamVolume:"
+ + cacheGetStreamVolume());
}
private void dumpAudioMode(PrintWriter pw) {
@@ -7054,6 +7063,13 @@ public class AudioService extends IAudioService.Stub
streamState.mIsMuted = false;
}
}
+ if (cacheGetStreamVolume()) {
+ if (DEBUG_VOL) {
+ Log.d(TAG,
+ "Clear volume cache after possibly changing mute in readAudioSettings");
+ }
+ AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API);
+ }
}
readVolumeGroupsSettings(userSwitch);
@@ -9274,11 +9290,23 @@ public class AudioService extends IAudioService.Stub
public void put(int key, int value) {
super.put(key, value);
record("put", key, value);
+ if (cacheGetStreamVolume()) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "Clear volume cache after update index map");
+ }
+ AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API);
+ }
}
@Override
public void setValueAt(int index, int value) {
super.setValueAt(index, value);
record("setValueAt", keyAt(index), value);
+ if (cacheGetStreamVolume()) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "Clear volume cache after update index map");
+ }
+ AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API);
+ }
}
// Record all changes in the VolumeStreamState
@@ -10008,8 +10036,9 @@ public class AudioService extends IAudioService.Stub
* @return true if the mute state was changed
*/
public boolean mute(boolean state, boolean apply, String src) {
+ boolean changed;
synchronized (VolumeStreamState.class) {
- boolean changed = state != mIsMuted;
+ changed = state != mIsMuted;
if (changed) {
sMuteLogger.enqueue(
new AudioServiceEvents.StreamMuteEvent(mStreamType, state, src));
@@ -10027,8 +10056,16 @@ public class AudioService extends IAudioService.Stub
doMute();
}
}
- return changed;
}
+
+ if (cacheGetStreamVolume() && changed) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "Clear volume cache after changing mute state");
+ }
+ AudioManager.clearVolumeCache(AudioManager.VOLUME_CACHING_API);
+ }
+
+ return changed;
}
public void doMute() {
diff --git a/services/core/java/com/android/server/display/plugin/PluginManager.java b/services/core/java/com/android/server/display/plugin/PluginManager.java
index d4099975cafa..cb0a4574361a 100644
--- a/services/core/java/com/android/server/display/plugin/PluginManager.java
+++ b/services/core/java/com/android/server/display/plugin/PluginManager.java
@@ -74,15 +74,17 @@ public class PluginManager {
/**
* Adds change listener for particular plugin type
*/
- public <T> void subscribe(PluginType<T> type, PluginChangeListener<T> listener) {
- mPluginStorage.addListener(type, listener);
+ public <T> void subscribe(PluginType<T> type, String uniqueDisplayId,
+ PluginChangeListener<T> listener) {
+ mPluginStorage.addListener(type, uniqueDisplayId, listener);
}
/**
* Removes change listener
*/
- public <T> void unsubscribe(PluginType<T> type, PluginChangeListener<T> listener) {
- mPluginStorage.removeListener(type, listener);
+ public <T> void unsubscribe(PluginType<T> type, String uniqueDisplayId,
+ PluginChangeListener<T> listener) {
+ mPluginStorage.removeListener(type, uniqueDisplayId, listener);
}
/**
diff --git a/services/core/java/com/android/server/display/plugin/PluginStorage.java b/services/core/java/com/android/server/display/plugin/PluginStorage.java
index dd3415fb614d..5102c2709329 100644
--- a/services/core/java/com/android/server/display/plugin/PluginStorage.java
+++ b/services/core/java/com/android/server/display/plugin/PluginStorage.java
@@ -20,10 +20,13 @@ import android.annotation.Nullable;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -35,42 +38,97 @@ import java.util.Set;
public class PluginStorage {
private static final String TAG = "PluginStorage";
+ // Special ID used to indicate that given value is to be applied globally, rather than to a
+ // specific display. If both GLOBAL and specific display values are present - specific display
+ // value is selected.
+ @VisibleForTesting
+ static final String GLOBAL_ID = "GLOBAL";
+
private final Object mLock = new Object();
@GuardedBy("mLock")
- private final Map<PluginType<?>, Object> mValues = new HashMap<>();
+ private final Map<PluginType<?>, ValuesContainer<?>> mValues = new HashMap<>();
@GuardedBy("mLock")
private final Map<PluginType<?>, ListenersContainer<?>> mListeners = new HashMap<>();
@GuardedBy("mLock")
- private final PluginEventStorage mPluginEventStorage = new PluginEventStorage();
+ private final Map<String, PluginEventStorage> mPluginEventStorages = new HashMap<>();
+
+ /**
+ * Updates value in storage and forwards it to corresponding listeners for all displays
+ * that does not have display specific value.
+ * Should be called by OEM Plugin implementation in order to communicate with Framework
+ */
+ @KeepForApi
+ public <T> void updateGlobalValue(PluginType<T> type, @Nullable T value) {
+ updateValue(type, GLOBAL_ID, value);
+ }
/**
- * Updates value in storage and forwards it to corresponding listeners.
- * Should be called by OEM Plugin implementation in order to provide communicate with Framework
+ * Updates value in storage and forwards it to corresponding listeners for specific display.
+ * Should be called by OEM Plugin implementation in order to communicate with Framework
+ * @param type - plugin type, that need to be updated
+ * @param uniqueDisplayId - uniqueDisplayId that this type/value should be applied to
+ * @param value - plugin value for particular type and display
*/
@KeepForApi
- public <T> void updateValue(PluginType<T> type, @Nullable T value) {
- Slog.d(TAG, "updateValue, type=" + type.mName + "; value=" + value);
+ public <T> void updateValue(PluginType<T> type, String uniqueDisplayId, @Nullable T value) {
+ Slog.d(TAG, "updateValue, type=" + type.mName + "; value=" + value
+ + "; displayId=" + uniqueDisplayId);
Set<PluginManager.PluginChangeListener<T>> localListeners;
+ T valueToNotify;
synchronized (mLock) {
- mValues.put(type, value);
- mPluginEventStorage.onValueUpdated(type);
- ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
- localListeners = new LinkedHashSet<>(container.mListeners);
+ ValuesContainer<T> valuesByType = getValuesContainerLocked(type);
+ valuesByType.updateValueLocked(uniqueDisplayId, value);
+ // if value was set to null, we might need to notify with GLOBAL value instead
+ valueToNotify = valuesByType.getValueLocked(uniqueDisplayId);
+
+ PluginEventStorage storage = mPluginEventStorages.computeIfAbsent(uniqueDisplayId,
+ d -> new PluginEventStorage());
+ storage.onValueUpdated(type);
+
+ localListeners = getListenersForUpdateLocked(type, uniqueDisplayId);
}
Slog.d(TAG, "updateValue, notifying listeners=" + localListeners);
- localListeners.forEach(l -> l.onChanged(value));
+ localListeners.forEach(l -> l.onChanged(valueToNotify));
+ }
+
+ @GuardedBy("mLock")
+ private <T> Set<PluginManager.PluginChangeListener<T>> getListenersForUpdateLocked(
+ PluginType<T> type, String uniqueDisplayId) {
+ ListenersContainer<T> listenersContainer = getListenersContainerLocked(type);
+ Set<PluginManager.PluginChangeListener<T>> localListeners = new LinkedHashSet<>();
+ // if GLOBAL value change we need to notify only listeners for displays that does not
+ // have display specific value
+ if (GLOBAL_ID.equals(uniqueDisplayId)) {
+ ValuesContainer<T> valuesContainer = getValuesContainerLocked(type);
+ Set<String> excludedDisplayIds = valuesContainer.getNonGlobalDisplaysLocked();
+ listenersContainer.mListeners.forEach((localDisplayId, listeners) -> {
+ if (!excludedDisplayIds.contains(localDisplayId)) {
+ localListeners.addAll(listeners);
+ }
+ });
+ } else {
+ localListeners.addAll(
+ listenersContainer.mListeners.getOrDefault(uniqueDisplayId, Set.of()));
+ }
+ return localListeners;
}
/**
* Adds listener for PluginType. If storage already has value for this type, listener will
* be notified immediately.
*/
- <T> void addListener(PluginType<T> type, PluginManager.PluginChangeListener<T> listener) {
+ <T> void addListener(PluginType<T> type, String uniqueDisplayId,
+ PluginManager.PluginChangeListener<T> listener) {
+ if (GLOBAL_ID.equals(uniqueDisplayId)) {
+ Slog.d(TAG, "addListener ignored for GLOBAL_ID, type=" + type.mName);
+ return;
+ }
T value = null;
synchronized (mLock) {
- ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
- if (container.mListeners.add(listener)) {
- value = getValueForTypeLocked(type);
+ ListenersContainer<T> container = getListenersContainerLocked(type);
+ if (container.addListenerLocked(uniqueDisplayId, listener)) {
+ ValuesContainer<T> valuesContainer = getValuesContainerLocked(type);
+ value = valuesContainer.getValueLocked(uniqueDisplayId);
}
}
if (value != null) {
@@ -81,10 +139,15 @@ public class PluginStorage {
/**
* Removes listener
*/
- <T> void removeListener(PluginType<T> type, PluginManager.PluginChangeListener<T> listener) {
+ <T> void removeListener(PluginType<T> type, String uniqueDisplayId,
+ PluginManager.PluginChangeListener<T> listener) {
+ if (GLOBAL_ID.equals(uniqueDisplayId)) {
+ Slog.d(TAG, "removeListener ignored for GLOBAL_ID, type=" + type.mName);
+ return;
+ }
synchronized (mLock) {
- ListenersContainer<T> container = getListenersContainerForTypeLocked(type);
- container.mListeners.remove(listener);
+ ListenersContainer<T> container = getListenersContainerLocked(type);
+ container.removeListenerLocked(uniqueDisplayId, listener);
}
}
@@ -92,53 +155,106 @@ public class PluginStorage {
* Print the object's state and debug information into the given stream.
*/
void dump(PrintWriter pw) {
- Map<PluginType<?>, Object> localValues;
+ Map<PluginType<?>, Map<String, Object>> localValues = new HashMap<>();
@SuppressWarnings("rawtypes")
- Map<PluginType, Set> localListeners = new HashMap<>();
- List<PluginEventStorage.TimeFrame> timeFrames;
+ Map<PluginType, Map<String, Set>> localListeners = new HashMap<>();
+ Map<String, List<PluginEventStorage.TimeFrame>> timeFrames = new HashMap<>();
synchronized (mLock) {
- timeFrames = mPluginEventStorage.getTimeFrames();
- localValues = new HashMap<>(mValues);
- mListeners.forEach((type, container) -> localListeners.put(type, container.mListeners));
+ mPluginEventStorages.forEach((displayId, storage) -> {
+ timeFrames.put(displayId, storage.getTimeFrames());
+ });
+ mValues.forEach((type, valueContainer) -> {
+ localValues.put(type, new HashMap<>(valueContainer.mValues));
+ });
+ mListeners.forEach((type, container) -> {
+ localListeners.put(type, new HashMap<>(container.mListeners));
+ });
}
pw.println("PluginStorage:");
pw.println("values=" + localValues);
pw.println("listeners=" + localListeners);
pw.println("PluginEventStorage:");
- for (PluginEventStorage.TimeFrame timeFrame: timeFrames) {
- timeFrame.dump(pw);
+ for (Map.Entry<String, List<PluginEventStorage.TimeFrame>> timeFrameEntry :
+ timeFrames.entrySet()) {
+ pw.println("TimeFrames for displayId=" + timeFrameEntry.getKey());
+ for (PluginEventStorage.TimeFrame timeFrame : timeFrameEntry.getValue()) {
+ timeFrame.dump(pw);
+ }
}
}
@GuardedBy("mLock")
@SuppressWarnings("unchecked")
- private <T> T getValueForTypeLocked(PluginType<T> type) {
- Object value = mValues.get(type);
- if (value == null) {
- return null;
- } else if (type.mType == value.getClass()) {
- return (T) value;
+ private <T> ListenersContainer<T> getListenersContainerLocked(PluginType<T> type) {
+ ListenersContainer<?> container = mListeners.get(type);
+ if (container == null) {
+ ListenersContainer<T> lc = new ListenersContainer<>();
+ mListeners.put(type, lc);
+ return lc;
} else {
- Slog.d(TAG, "getValueForType: unexpected value type=" + value.getClass().getName()
- + ", expected=" + type.mType.getName());
- return null;
+ return (ListenersContainer<T>) container;
}
}
@GuardedBy("mLock")
@SuppressWarnings("unchecked")
- private <T> ListenersContainer<T> getListenersContainerForTypeLocked(PluginType<T> type) {
- ListenersContainer<?> container = mListeners.get(type);
+ private <T> ValuesContainer<T> getValuesContainerLocked(PluginType<T> type) {
+ ValuesContainer<?> container = mValues.get(type);
if (container == null) {
- ListenersContainer<T> lc = new ListenersContainer<>();
- mListeners.put(type, lc);
- return lc;
+ ValuesContainer<T> vc = new ValuesContainer<>();
+ mValues.put(type, vc);
+ return vc;
} else {
- return (ListenersContainer<T>) container;
+ return (ValuesContainer<T>) container;
}
}
private static final class ListenersContainer<T> {
- private final Set<PluginManager.PluginChangeListener<T>> mListeners = new LinkedHashSet<>();
+ private final Map<String, Set<PluginManager.PluginChangeListener<T>>> mListeners =
+ new LinkedHashMap<>();
+
+ private boolean addListenerLocked(
+ String uniqueDisplayId, PluginManager.PluginChangeListener<T> listener) {
+ Set<PluginManager.PluginChangeListener<T>> listenersForDisplay =
+ mListeners.computeIfAbsent(uniqueDisplayId, k -> new LinkedHashSet<>());
+ return listenersForDisplay.add(listener);
+ }
+
+ private void removeListenerLocked(String uniqueDisplayId,
+ PluginManager.PluginChangeListener<T> listener) {
+ Set<PluginManager.PluginChangeListener<T>> listenersForDisplay = mListeners.get(
+ uniqueDisplayId);
+ if (listenersForDisplay == null) {
+ return;
+ }
+
+ listenersForDisplay.remove(listener);
+
+ if (listenersForDisplay.isEmpty()) {
+ mListeners.remove(uniqueDisplayId);
+ }
+ }
+ }
+
+ private static final class ValuesContainer<T> {
+ private final Map<String, T> mValues = new HashMap<>();
+
+ private void updateValueLocked(String uniqueDisplayId, @Nullable T value) {
+ if (value == null) {
+ mValues.remove(uniqueDisplayId);
+ } else {
+ mValues.put(uniqueDisplayId, value);
+ }
+ }
+
+ private Set<String> getNonGlobalDisplaysLocked() {
+ Set<String> keys = new HashSet<>(mValues.keySet());
+ keys.remove(GLOBAL_ID);
+ return keys;
+ }
+
+ private @Nullable T getValueLocked(String displayId) {
+ return mValues.getOrDefault(displayId, mValues.get(GLOBAL_ID));
+ }
}
}
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index be8a94149fdc..32b36bfb50e5 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -23,7 +23,6 @@ import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut;
-import static com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,6 +36,7 @@ import android.os.SystemProperties;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import android.view.KeyEvent;
+import android.window.DesktopModeFlags;
import com.android.internal.annotations.GuardedBy;
@@ -233,7 +233,7 @@ final class InputGestureManager {
KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS));
}
- if (enableTaskResizingKeyboardShortcuts()) {
+ if (DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue()) {
systemShortcuts.add(createKeyGesture(
KeyEvent.KEYCODE_LEFT_BRACKET,
KeyEvent.META_META_ON,
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
index 2e0bb4f88485..18f2f48b80a3 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionStopController.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.telecom.TelecomManager;
import android.telephony.TelephonyCallback;
@@ -38,6 +39,7 @@ import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.SystemConfig;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -60,21 +62,35 @@ public class MediaProjectionStopController {
private final TelephonyManager mTelephonyManager;
private final AppOpsManager mAppOpsManager;
private final PackageManager mPackageManager;
- private final RoleManager mRoleManager;
+ private final RoleHolderProvider mRoleHolderProvider;
private final ContentResolver mContentResolver;
private boolean mIsInCall;
private long mLastCallStartTimeMillis;
+
+ @VisibleForTesting
+ interface RoleHolderProvider {
+ List<String> getRoleHoldersAsUser(String roleName, UserHandle user);
+ }
+
public MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer) {
+ this(context, stopReasonConsumer,
+ (roleName, user) -> context.getSystemService(RoleManager.class)
+ .getRoleHoldersAsUser(roleName, user));
+ }
+
+ @VisibleForTesting
+ MediaProjectionStopController(Context context, Consumer<Integer> stopReasonConsumer,
+ RoleHolderProvider roleHolderProvider) {
mStopReasonConsumer = stopReasonConsumer;
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mTelecomManager = context.getSystemService(TelecomManager.class);
mTelephonyManager = context.getSystemService(TelephonyManager.class);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mPackageManager = context.getPackageManager();
- mRoleManager = context.getSystemService(RoleManager.class);
mContentResolver = context.getContentResolver();
+ mRoleHolderProvider = roleHolderProvider;
}
/**
@@ -146,8 +162,9 @@ public class MediaProjectionStopController {
Slog.v(TAG, "Continuing MediaProjection for package with OP_PROJECT_MEDIA AppOp ");
return true;
}
- if (mRoleManager.getRoleHoldersAsUser(AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- projectionGrant.userHandle).contains(projectionGrant.packageName)) {
+ if (mRoleHolderProvider.getRoleHoldersAsUser(
+ AssociationRequest.DEVICE_PROFILE_APP_STREAMING, projectionGrant.userHandle)
+ .contains(projectionGrant.packageName)) {
Slog.v(TAG, "Continuing MediaProjection for package holding app streaming role.");
return true;
}
@@ -177,10 +194,6 @@ public class MediaProjectionStopController {
*/
public boolean isStartForbidden(
MediaProjectionManagerService.MediaProjection projectionGrant) {
- if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
- return false;
- }
-
if (!mKeyguardManager.isKeyguardLocked()) {
return false;
}
@@ -194,9 +207,6 @@ public class MediaProjectionStopController {
@VisibleForTesting
void onKeyguardLockedStateChanged(boolean isKeyguardLocked) {
if (!isKeyguardLocked) return;
- if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
- return;
- }
mStopReasonConsumer.accept(STOP_REASON_KEYGUARD);
}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 5259bcc78311..f6c94a7d9a5a 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -32,10 +32,16 @@ import android.hardware.tv.mediaquality.AmbientBacklightColorFormat;
import android.hardware.tv.mediaquality.DolbyAudioProcessing;
import android.hardware.tv.mediaquality.DtsVirtualX;
import android.hardware.tv.mediaquality.IMediaQuality;
+import android.hardware.tv.mediaquality.IPictureProfileAdjustmentListener;
+import android.hardware.tv.mediaquality.IPictureProfileChangedListener;
+import android.hardware.tv.mediaquality.ISoundProfileAdjustmentListener;
+import android.hardware.tv.mediaquality.ISoundProfileChangedListener;
+import android.hardware.tv.mediaquality.ParamCapability;
import android.hardware.tv.mediaquality.PictureParameter;
import android.hardware.tv.mediaquality.PictureParameters;
import android.hardware.tv.mediaquality.SoundParameter;
import android.hardware.tv.mediaquality.SoundParameters;
+import android.hardware.tv.mediaquality.VendorParamCapability;
import android.media.quality.AmbientBacklightEvent;
import android.media.quality.AmbientBacklightMetadata;
import android.media.quality.AmbientBacklightSettings;
@@ -105,6 +111,10 @@ public class MediaQualityService extends SystemService {
private final BiMap<Long, String> mPictureProfileTempIdMap;
private final BiMap<Long, String> mSoundProfileTempIdMap;
private IMediaQuality mMediaQuality;
+ private IPictureProfileAdjustmentListener mPpAdjustmentListener;
+ private ISoundProfileAdjustmentListener mSpAdjustmentListener;
+ private IPictureProfileChangedListener mPpChangedListener;
+ private ISoundProfileChangedListener mSpChangedListener;
private final HalAmbientBacklightCallback mHalAmbientBacklightCallback;
private final Map<String, AmbientBacklightCallbackRecord> mCallbackRecords = new HashMap<>();
private final PackageManager mPackageManager;
@@ -140,18 +150,104 @@ public class MediaQualityService extends SystemService {
@Override
public void onStart() {
IBinder binder = ServiceManager.getService(IMediaQuality.DESCRIPTOR + "/default");
- if (binder != null) {
- Slogf.d(TAG, "binder is not null");
- mMediaQuality = IMediaQuality.Stub.asInterface(binder);
- if (mMediaQuality != null) {
- try {
- mMediaQuality.setAmbientBacklightCallback(mHalAmbientBacklightCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to set ambient backlight detector callback", e);
+ if (binder == null) {
+ Slogf.d(TAG, "Binder is null");
+ return;
+ }
+ Slogf.d(TAG, "Binder is not null");
+
+ mPpAdjustmentListener = new IPictureProfileAdjustmentListener.Stub() {
+ @Override
+ public void onPictureProfileAdjusted(
+ android.hardware.tv.mediaquality.PictureProfile pictureProfile)
+ throws RemoteException {
+ // TODO
+ }
+
+ @Override
+ public void onParamCapabilityChanged(long pictureProfileId, ParamCapability[] caps)
+ throws RemoteException {
+ // TODO
+ }
+
+ @Override
+ public void onVendorParamCapabilityChanged(long pictureProfileId,
+ VendorParamCapability[] caps) throws RemoteException {
+ // TODO
+ }
+
+ @Override
+ public void requestPictureParameters(long pictureProfileId) throws RemoteException {
+ // TODO
+ }
+
+ @Override
+ public void onStreamStatusChanged(long pictureProfileId, byte status)
+ throws RemoteException {
+ // TODO
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
}
+ };
+ mSpAdjustmentListener = new ISoundProfileAdjustmentListener.Stub() {
+
+ @Override
+ public void onSoundProfileAdjusted(
+ android.hardware.tv.mediaquality.SoundProfile soundProfile)
+ throws RemoteException {
+ // TODO
+ }
+
+ @Override
+ public void onParamCapabilityChanged(long soundProfileId, ParamCapability[] caps)
+ throws RemoteException {
+ // TODO
+ }
+
+ @Override
+ public void onVendorParamCapabilityChanged(long soundProfileId,
+ VendorParamCapability[] caps) throws RemoteException {
+ // TODO
+ }
+
+ @Override
+ public void requestSoundParameters(long soundProfileId) throws RemoteException {
+ // TODO
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return null;
+ }
+ };
+
+ mMediaQuality = IMediaQuality.Stub.asInterface(binder);
+ if (mMediaQuality != null) {
+ try {
+ mMediaQuality.setAmbientBacklightCallback(mHalAmbientBacklightCallback);
+ mMediaQuality.setPictureProfileAdjustmentListener(mPpAdjustmentListener);
+ mMediaQuality.setSoundProfileAdjustmentListener(mSpAdjustmentListener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set ambient backlight detector callback", e);
}
}
+ mPpChangedListener = IPictureProfileChangedListener.Stub.asInterface(binder);
+ mSpChangedListener = ISoundProfileChangedListener.Stub.asInterface(binder);
+
publishBinderService(Context.MEDIA_QUALITY_SERVICE, new BinderService());
}
@@ -187,6 +283,30 @@ public class MediaQualityService extends SystemService {
return pp;
}
+ private void notifyHalOnPictureProfileChange(Long dbId, PersistableBundle params) {
+ // TODO: only notify HAL when the profile is active / being used
+ try {
+ mPpChangedListener.onPictureProfileChanged(convertToHalPictureProfile(dbId,
+ params));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify HAL on picture profile change.", e);
+ }
+ }
+
+ private android.hardware.tv.mediaquality.PictureProfile convertToHalPictureProfile(Long id,
+ PersistableBundle params) {
+ PictureParameters pictureParameters = new PictureParameters();
+ pictureParameters.pictureParameters = convertPersistableBundleToPictureParameterList(
+ params);
+
+ android.hardware.tv.mediaquality.PictureProfile toReturn =
+ new android.hardware.tv.mediaquality.PictureProfile();
+ toReturn.pictureProfileId = id;
+ toReturn.parameters = pictureParameters;
+
+ return toReturn;
+ }
+
@Override
public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
Long dbId = mPictureProfileTempIdMap.getKey(id);
@@ -207,6 +327,7 @@ public class MediaQualityService extends SystemService {
null, values);
notifyOnPictureProfileUpdated(mPictureProfileTempIdMap.getValue(dbId),
getPictureProfile(dbId), Binder.getCallingUid(), Binder.getCallingPid());
+ notifyHalOnPictureProfileChange(dbId, pp.getParameters());
}
private boolean hasPermissionToUpdatePictureProfile(Long dbId, PictureProfile toUpdate) {
@@ -240,6 +361,7 @@ public class MediaQualityService extends SystemService {
notifyOnPictureProfileRemoved(mPictureProfileTempIdMap.getValue(dbId), toDelete,
Binder.getCallingUid(), Binder.getCallingPid());
mPictureProfileTempIdMap.remove(dbId);
+ notifyHalOnPictureProfileChange(dbId, null);
}
}
@@ -359,6 +481,10 @@ public class MediaQualityService extends SystemService {
private PictureParameter[] convertPersistableBundleToPictureParameterList(
PersistableBundle params) {
+ if (params == null) {
+ return null;
+ }
+
List<PictureParameter> pictureParams = new ArrayList<>();
if (params.containsKey(PictureQuality.PARAMETER_BRIGHTNESS)) {
pictureParams.add(PictureParameter.brightness(params.getLong(
@@ -677,10 +803,6 @@ public class MediaQualityService extends SystemService {
pictureParams.add(PictureParameter.colorTunerLuminanceFlesh(params.getInt(
PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)));
}
- if (params.containsKey(PictureQuality.PARAMETER_ACTIVE_PROFILE)) {
- pictureParams.add(PictureParameter.activeProfile(params.getBoolean(
- PictureQuality.PARAMETER_ACTIVE_PROFILE)));
- }
if (params.containsKey(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)) {
pictureParams.add(PictureParameter.pictureQualityEventType(
(byte) params.getInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)));
@@ -759,6 +881,28 @@ public class MediaQualityService extends SystemService {
return sp;
}
+ private void notifyHalOnSoundProfileChange(Long dbId, PersistableBundle params) {
+ // TODO: only notify HAL when the profile is active / being used
+ try {
+ mSpChangedListener.onSoundProfileChanged(convertToHalSoundProfile(dbId, params));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to notify HAL on sound profile change.", e);
+ }
+ }
+
+ private android.hardware.tv.mediaquality.SoundProfile convertToHalSoundProfile(Long id,
+ PersistableBundle params) {
+ SoundParameters soundParameters = new SoundParameters();
+ soundParameters.soundParameters = convertPersistableBundleToSoundParameterList(params);
+
+ android.hardware.tv.mediaquality.SoundProfile toReturn =
+ new android.hardware.tv.mediaquality.SoundProfile();
+ toReturn.soundProfileId = id;
+ toReturn.parameters = soundParameters;
+
+ return toReturn;
+ }
+
@Override
public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) {
Long dbId = mSoundProfileTempIdMap.getKey(id);
@@ -778,6 +922,7 @@ public class MediaQualityService extends SystemService {
db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
notifyOnSoundProfileUpdated(mSoundProfileTempIdMap.getValue(dbId),
getSoundProfile(dbId), Binder.getCallingUid(), Binder.getCallingPid());
+ notifyHalOnSoundProfileChange(dbId, sp.getParameters());
}
private boolean hasPermissionToUpdateSoundProfile(Long dbId, SoundProfile sp) {
@@ -810,6 +955,7 @@ public class MediaQualityService extends SystemService {
notifyOnSoundProfileRemoved(mSoundProfileTempIdMap.getValue(dbId), toDelete,
Binder.getCallingUid(), Binder.getCallingPid());
mSoundProfileTempIdMap.remove(dbId);
+ notifyHalOnSoundProfileChange(dbId, null);
}
}
@@ -929,6 +1075,9 @@ public class MediaQualityService extends SystemService {
private SoundParameter[] convertPersistableBundleToSoundParameterList(
PersistableBundle params) {
//TODO: set EqualizerDetail
+ if (params == null) {
+ return null;
+ }
List<SoundParameter> soundParams = new ArrayList<>();
if (params.containsKey(SoundQuality.PARAMETER_BALANCE)) {
soundParams.add(SoundParameter.balance(params.getInt(
@@ -973,10 +1122,6 @@ public class MediaQualityService extends SystemService {
soundParams.add(SoundParameter.downmixMode((byte) params.getInt(
SoundQuality.PARAMETER_DOWN_MIX_MODE)));
}
- if (params.containsKey(SoundQuality.PARAMETER_ACTIVE_PROFILE)) {
- soundParams.add(SoundParameter.activeProfile(params.getBoolean(
- SoundQuality.PARAMETER_ACTIVE_PROFILE)));
- }
if (params.containsKey(SoundQuality.PARAMETER_SOUND_STYLE)) {
soundParams.add(SoundParameter.soundStyle((byte) params.getInt(
SoundQuality.PARAMETER_SOUND_STYLE)));
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
index 25c07500b891..872ab595994b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
@@ -28,6 +28,9 @@ public abstract class WallpaperManagerInternal {
*/
public abstract void onDisplayReady(int displayId);
+ /** Notifies when display stop showing system decorations and wallpaper. */
+ public abstract void onDisplayRemoveSystemDecorations(int displayId);
+
/** Notifies when the screen finished turning on and is visible to the user. */
public abstract void onScreenTurnedOn(int displayId);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 83e146df3e53..db530e728a1a 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -667,71 +667,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
@Override
public void onDisplayRemoved(int displayId) {
- synchronized (mLock) {
- if (enableConnectedDisplaysWallpaper()) {
- // There could be at most 2 wallpaper connections per display:
- // 1. system & lock are the same: mLastWallpaper
- // 2. system, lock are different: mLastWallpaper, mLastLockWallpaper
- // 3. fallback used as both system & lock wallpaper: mFallbackWallpaper
- // 4. fallback used as lock only wallpaper: mFallbackWallpaper,
- // mLastWallpaper
- // 5. fallback used as system only wallpaper: mFallbackWallpaper,
- // mLastLockWallpaper
- List<WallpaperData> pendingDisconnectWallpapers = new ArrayList<>();
- if (mLastWallpaper != null && mLastWallpaper.connection != null
- && mLastWallpaper.connection.containsDisplay(displayId)) {
- pendingDisconnectWallpapers.add(mLastWallpaper);
- }
- if (mLastLockWallpaper != null && mLastLockWallpaper.connection != null
- && mLastLockWallpaper.connection.containsDisplay(displayId)) {
- pendingDisconnectWallpapers.add(mLastLockWallpaper);
- }
- if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null
- && mFallbackWallpaper.connection.containsDisplay(displayId)) {
- pendingDisconnectWallpapers.add(mFallbackWallpaper);
- }
- for (int i = 0; i < pendingDisconnectWallpapers.size(); i++) {
- WallpaperData wallpaper = pendingDisconnectWallpapers.get(i);
- DisplayConnector displayConnector =
- wallpaper.connection.getDisplayConnectorOrCreate(displayId);
- if (displayConnector == null) {
- Slog.w(TAG,
- "Fail to disconnect wallpaper upon display removal");
- return;
- }
- displayConnector.disconnectLocked(wallpaper.connection);
- wallpaper.connection.removeDisplayConnector(displayId);
- }
- } else {
- if (mLastWallpaper != null) {
- WallpaperData targetWallpaper = null;
- if (mLastWallpaper.connection != null
- && mLastWallpaper.connection.containsDisplay(displayId)) {
- targetWallpaper = mLastWallpaper;
- } else if (mFallbackWallpaper != null
- && mFallbackWallpaper.connection != null
- && mFallbackWallpaper.connection.containsDisplay(
- displayId)) {
- targetWallpaper = mFallbackWallpaper;
- }
- if (targetWallpaper == null) return;
- DisplayConnector connector =
- targetWallpaper.connection.getDisplayConnectorOrCreate(
- displayId);
- if (connector == null) return;
- connector.disconnectLocked(targetWallpaper.connection);
- targetWallpaper.connection.removeDisplayConnector(displayId);
- }
- }
-
- mWallpaperDisplayHelper.removeDisplayData(displayId);
-
- for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) {
- final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks =
- mColorsChangedListeners.valueAt(i);
- callbacks.delete(displayId);
- }
- }
+ onDisplayRemovedInternal(displayId);
}
@Override
@@ -1705,6 +1641,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
@Override
+ public void onDisplayRemoveSystemDecorations(int displayId) {
+ // The display mirroring starts. The handling logic is the same as when removing a
+ // display.
+ onDisplayRemovedInternal(displayId);
+ }
+
+ @Override
public void onScreenTurnedOn(int displayId) {
notifyScreenTurnedOn(displayId);
}
@@ -4063,6 +4006,78 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
}
+ // This method may be called even if the display is not being removed from the system.
+ // This can be called when the display is removed, or when the display system decorations are
+ // removed to start mirroring.
+ private void onDisplayRemovedInternal(int displayId) {
+ synchronized (mLock) {
+ if (enableConnectedDisplaysWallpaper()) {
+ // There could be at most 2 wallpaper connections per display:
+ // 1. system & lock are the same: mLastWallpaper
+ // 2. system, lock are different: mLastWallpaper, mLastLockWallpaper
+ // 3. fallback used as both system & lock wallpaper: mFallbackWallpaper
+ // 4. fallback used as lock only wallpaper: mFallbackWallpaper,
+ // mLastWallpaper
+ // 5. fallback used as system only wallpaper: mFallbackWallpaper,
+ // mLastLockWallpaper
+ List<WallpaperData> pendingDisconnectWallpapers = new ArrayList<>();
+ if (mLastWallpaper != null && mLastWallpaper.connection != null
+ && mLastWallpaper.connection.containsDisplay(displayId)) {
+ pendingDisconnectWallpapers.add(mLastWallpaper);
+ }
+ if (mLastLockWallpaper != null && mLastLockWallpaper.connection != null
+ && mLastLockWallpaper.connection.containsDisplay(displayId)) {
+ pendingDisconnectWallpapers.add(mLastLockWallpaper);
+ }
+ if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null
+ && mFallbackWallpaper.connection.containsDisplay(displayId)) {
+ pendingDisconnectWallpapers.add(mFallbackWallpaper);
+ }
+ for (int i = 0; i < pendingDisconnectWallpapers.size(); i++) {
+ WallpaperData wallpaper = pendingDisconnectWallpapers.get(i);
+ DisplayConnector displayConnector =
+ wallpaper.connection.getDisplayConnectorOrCreate(displayId);
+ if (displayConnector == null) {
+ Slog.w(TAG,
+ "Fail to disconnect wallpaper upon display removes system "
+ + "decorations");
+ return;
+ }
+ displayConnector.disconnectLocked(wallpaper.connection);
+ wallpaper.connection.removeDisplayConnector(displayId);
+ }
+ } else {
+ if (mLastWallpaper != null) {
+ WallpaperData targetWallpaper = null;
+ if (mLastWallpaper.connection != null
+ && mLastWallpaper.connection.containsDisplay(displayId)) {
+ targetWallpaper = mLastWallpaper;
+ } else if (mFallbackWallpaper != null
+ && mFallbackWallpaper.connection != null
+ && mFallbackWallpaper.connection.containsDisplay(
+ displayId)) {
+ targetWallpaper = mFallbackWallpaper;
+ }
+ if (targetWallpaper == null) return;
+ DisplayConnector connector =
+ targetWallpaper.connection.getDisplayConnectorOrCreate(
+ displayId);
+ if (connector == null) return;
+ connector.disconnectLocked(targetWallpaper.connection);
+ targetWallpaper.connection.removeDisplayConnector(displayId);
+ }
+ }
+
+ mWallpaperDisplayHelper.removeDisplayData(displayId);
+
+ for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) {
+ final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks =
+ mColorsChangedListeners.valueAt(i);
+ callbacks.delete(displayId);
+ }
+ }
+ }
+
void saveSettingsLocked(int userId) {
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
t.traceBegin("WPMS.saveSettingsLocked-" + userId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 064ef1aa0eff..89b46bc4eba4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -46,6 +46,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.isFloating;
import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE;
import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
@@ -8376,6 +8377,7 @@ final class ActivityRecord extends WindowToken {
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
+ // TODO(b/392069771): Move to AppCompatSandboxingPolicy.
// Sandbox max bounds by setting it to the activity bounds, if activity is letterboxed, or
// has or will have mAppCompatDisplayInsets for size compat. Also forces an activity to be
// sandboxed or not depending upon the configuration settings.
@@ -8404,6 +8406,20 @@ final class ActivityRecord extends WindowToken {
resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
}
+ // Sandbox activity bounds in freeform to app bounds to force app to display within the
+ // container. This prevents UI cropping when activities can draw below insets which are
+ // normally excluded from appBounds before targetSDK < 35
+ // (see ConfigurationContainer#applySizeOverrideIfNeeded).
+ if (isFloating(parentWindowingMode)) {
+ Rect appBounds = resolvedConfig.windowConfiguration.getAppBounds();
+ if (appBounds == null || appBounds.isEmpty()) {
+ // When there is no override bounds, the activity will inherit the bounds from
+ // parent.
+ appBounds = mResolveConfigHint.mParentAppBoundsOverride;
+ }
+ resolvedConfig.windowConfiguration.setBounds(appBounds);
+ }
+
applySizeOverrideIfNeeded(
mDisplayContent,
info.applicationInfo,
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index 35fa39dab900..d994a1904a14 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -180,10 +180,7 @@ class AppCompatOrientationPolicy {
return true;
}
- final AppCompatCameraPolicy cameraPolicy = AppCompatCameraPolicy
- .getAppCompatCameraPolicy(mActivityRecord);
- if (cameraPolicy != null
- && cameraPolicy.isTreatmentEnabledForActivity(mActivityRecord)) {
+ if (AppCompatCameraPolicy.isTreatmentEnabledForActivity(mActivityRecord)) {
Slog.w(TAG, "Ignoring orientation update to "
+ screenOrientationToString(requestedOrientation)
+ " due to camera compat treatment for " + mActivityRecord);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e1a50a93edcc..5b1619995529 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1910,6 +1910,11 @@ public class DisplayPolicy {
if (statusBar != null) {
statusBar.onDisplayRemoveSystemDecorations(displayId);
}
+ final WallpaperManagerInternal wpMgr =
+ LocalServices.getService(WallpaperManagerInternal.class);
+ if (wpMgr != null) {
+ wpMgr.onDisplayRemoveSystemDecorations(displayId);
+ }
});
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index bcfaa3947e74..59ca79c7ffc3 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -361,7 +361,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
controlTarget = mDisplayContent.getImeHostOrFallback(
((InsetsControlTarget) imeInsetsTarget).getWindow());
- if (controlTarget != imeInsetsTarget) {
+ if (controlTarget != null && controlTarget != imeInsetsTarget) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
controlTarget.setImeInputTargetRequestedVisibility(imeVisible, statsToken);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 37cc0d22c063..27683b2fcff2 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1504,16 +1504,15 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
// Update the input-sink (touch-blocking) state now that the animation is finished.
- SurfaceControl.Transaction inputSinkTransaction = null;
+ boolean scheduleAnimation = false;
for (int i = 0; i < mParticipants.size(); ++i) {
final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
if (ar == null || !ar.isVisible() || ar.getParent() == null) continue;
- if (inputSinkTransaction == null) {
- inputSinkTransaction = ar.mWmService.mTransactionFactory.get();
- }
- ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(inputSinkTransaction);
+ scheduleAnimation = true;
+ ar.mActivityRecordInputSink.applyChangesToSurfaceIfChanged(ar.getPendingTransaction());
}
- if (inputSinkTransaction != null) inputSinkTransaction.apply();
+ // To apply pending transactions.
+ if (scheduleAnimation) mController.mAtm.mWindowManager.scheduleAnimationLocked();
// Always schedule stop processing when transition finishes because activities don't
// stop while they are in a transition thus their stop could still be pending.
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e761e024b3ca..883d8f95b612 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1680,26 +1680,27 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* Sets the specified orientation of this container. It percolates this change upward along the
* hierarchy to let each level of the hierarchy a chance to respond to it.
*
- * @param orientation the specified orientation. Needs to be one of {@link ScreenOrientation}.
+ * @param requestedOrientation the specified orientation. Needs to be one of
+ * {@link ScreenOrientation}.
* @param requestingContainer the container which orientation request has changed. Mostly used
* to ensure it gets correct configuration.
* @return the resolved override orientation of this window container.
*/
@ScreenOrientation
- int setOrientation(@ScreenOrientation int orientation,
+ int setOrientation(@ScreenOrientation int requestedOrientation,
@Nullable WindowContainer requestingContainer) {
- if (getOverrideOrientation() == orientation) {
- return orientation;
+ if (getOverrideOrientation() == requestedOrientation) {
+ return requestedOrientation;
}
- setOverrideOrientation(orientation);
+ setOverrideOrientation(requestedOrientation);
final WindowContainer parent = getParent();
if (parent == null) {
- return orientation;
+ return requestedOrientation;
}
// The derived class can return a result that is different from the given orientation.
- final int resolvedOrientation = getOverrideOrientation();
+ final int actualOverrideOrientation = getOverrideOrientation();
if (getConfiguration().orientation != getRequestedConfigurationOrientation(
- false /* forDisplay */, resolvedOrientation)
+ false /* forDisplay */, actualOverrideOrientation)
// Update configuration directly only if the change won't be dispatched from
// ancestor. This prevents from computing intermediate configuration when the
// parent also needs to be updated from the ancestor. E.g. the app requests
@@ -1707,12 +1708,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// the task can be updated to portrait first so the configuration can be
// computed in a consistent environment.
&& (inMultiWindowMode()
- || !handlesOrientationChangeFromDescendant(orientation))) {
+ || !handlesOrientationChangeFromDescendant(requestedOrientation))) {
// Resolve the requested orientation.
onConfigurationChanged(parent.getConfiguration());
}
onDescendantOrientationChanged(requestingContainer);
- return resolvedOrientation;
+ return actualOverrideOrientation;
}
@ScreenOrientation
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 073ee31ddd60..4f9859d33d74 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -51,7 +51,6 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.pm.UserManagerInternal;
import java.io.FileDescriptor;
@@ -62,17 +61,17 @@ import java.util.List;
public class SupervisionService extends ISupervisionManager.Stub {
private static final String LOG_TAG = "SupervisionService";
- private final Context mContext;
-
// TODO(b/362756788): Does this need to be a LockGuard lock?
private final Object mLockDoNoUseDirectly = new Object();
@GuardedBy("getLockObject()")
private final SparseArray<SupervisionUserData> mUserData = new SparseArray<>();
+ private final Context mContext;
private final DevicePolicyManagerInternal mDpmInternal;
private final PackageManager mPackageManager;
private final UserManagerInternal mUserManagerInternal;
+ final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl();
public SupervisionService(Context context) {
mContext = context.createAttributionContext(LOG_TAG);
@@ -82,6 +81,12 @@ public class SupervisionService extends ISupervisionManager.Stub {
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
}
+ /**
+ * Returns whether supervision is enabled for the given user.
+ *
+ * <p>Supervision is automatically enabled when the supervision app becomes the profile owner or
+ * explicitly enabled via an internal call to {@link #setSupervisionEnabledForUser}.
+ */
@Override
public boolean isSupervisionEnabledForUser(@UserIdInt int userId) {
if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
@@ -92,6 +97,20 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
}
+ /**
+ * Returns the package name of the active supervision app or null if supervision is disabled.
+ */
+ @Override
+ @Nullable
+ public String getActiveSupervisionAppPackage(@UserIdInt int userId) {
+ if (UserHandle.getUserId(Binder.getCallingUid()) != userId) {
+ enforcePermission(INTERACT_ACROSS_USERS);
+ }
+ synchronized (getLockObject()) {
+ return getUserDataLocked(userId).supervisionAppPackage;
+ }
+ }
+
@Override
public void onShellCommand(
@Nullable FileDescriptor in,
@@ -140,35 +159,53 @@ public class SupervisionService extends ISupervisionManager.Stub {
return data;
}
- void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
+ /**
+ * Sets supervision as enabled or disabled for the given user and, in case supervision is being
+ * enabled, the package of the active supervision app.
+ */
+ private void setSupervisionEnabledForUser(
+ @UserIdInt int userId, boolean enabled, @Nullable String supervisionAppPackage) {
synchronized (getLockObject()) {
- getUserDataLocked(userId).supervisionEnabled = enabled;
+ SupervisionUserData data = getUserDataLocked(userId);
+ data.supervisionEnabled = enabled;
+ data.supervisionAppPackage = enabled ? supervisionAppPackage : null;
}
}
- /** Ensures that supervision is enabled when supervision app is the profile owner. */
+ /** Ensures that supervision is enabled when the supervision app is the profile owner. */
private void syncStateWithDevicePolicyManager(@UserIdInt int userId) {
- if (isProfileOwner(userId)) {
- setSupervisionEnabledForUser(userId, true);
+ final ComponentName po =
+ mDpmInternal != null ? mDpmInternal.getProfileOwnerAsUser(userId) : null;
+
+ if (po != null && po.getPackageName().equals(getSystemSupervisionPackage())) {
+ setSupervisionEnabledForUser(userId, true, po.getPackageName());
+ } else if (po != null && po.equals(getSupervisionProfileOwnerComponent())) {
+ // TODO(b/392071637): Consider not enabling supervision in case profile owner is given
+ // to the legacy supervision profile owner component.
+ setSupervisionEnabledForUser(userId, true, po.getPackageName());
} else {
// TODO(b/381428475): Avoid disabling supervision when the app is not the profile owner.
// This might only be possible after introducing specific and public APIs to enable
- // supervision.
- setSupervisionEnabledForUser(userId, false);
+ // and disable supervision.
+ setSupervisionEnabledForUser(userId, false, /* supervisionAppPackage= */ null);
}
}
- /** Returns whether the supervision app has profile owner status. */
- private boolean isProfileOwner(@UserIdInt int userId) {
- ComponentName profileOwner =
- mDpmInternal != null ? mDpmInternal.getProfileOwnerAsUser(userId) : null;
- return profileOwner != null && isSupervisionAppPackage(profileOwner.getPackageName());
+ /**
+ * Returns the {@link ComponentName} of the supervision profile owner component.
+ *
+ * <p>This component is used to give GMS Kids Module permission to supervise the device and may
+ * still be active during the transition to the {@code SYSTEM_SUPERVISION} role.
+ */
+ private ComponentName getSupervisionProfileOwnerComponent() {
+ return ComponentName.unflattenFromString(
+ mContext.getResources()
+ .getString(R.string.config_defaultSupervisionProfileOwnerComponent));
}
- /** Returns whether the given package name belongs to the supervision role holder. */
- private boolean isSupervisionAppPackage(String packageName) {
- return packageName.equals(
- mContext.getResources().getString(R.string.config_systemSupervision));
+ /** Returns the package assigned to the {@code SYSTEM_SUPERVISION} role. */
+ private String getSystemSupervisionPackage() {
+ return mContext.getResources().getString(R.string.config_systemSupervision);
}
/** Enforces that the caller has the given permission. */
@@ -228,19 +265,21 @@ public class SupervisionService extends ISupervisionManager.Stub {
}
}
- final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl();
-
private final class SupervisionManagerInternalImpl extends SupervisionManagerInternal {
@Override
public boolean isActiveSupervisionApp(int uid) {
- String[] packages = mPackageManager.getPackagesForUid(uid);
- if (packages == null) {
+ int userId = UserHandle.getUserId(uid);
+ String supervisionAppPackage = getActiveSupervisionAppPackage(userId);
+ if (supervisionAppPackage == null) {
return false;
}
- for (var packageName : packages) {
- if (SupervisionService.this.isSupervisionAppPackage(packageName)) {
- int userId = UserHandle.getUserId(uid);
- return SupervisionService.this.isSupervisionEnabledForUser(userId);
+
+ String[] packages = mPackageManager.getPackagesForUid(uid);
+ if (packages != null) {
+ for (var packageName : packages) {
+ if (supervisionAppPackage.equals(packageName)) {
+ return true;
+ }
}
}
return false;
@@ -253,7 +292,8 @@ public class SupervisionService extends ISupervisionManager.Stub {
@Override
public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) {
- SupervisionService.this.setSupervisionEnabledForUser(userId, enabled);
+ SupervisionService.this.setSupervisionEnabledForUser(
+ userId, enabled, getSystemSupervisionPackage());
}
@Override
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
index 2adaae3943f1..976642bd563d 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
@@ -32,16 +32,18 @@ public class SupervisionServiceShellCommand extends ShellCommand {
return handleDefaultCommands(null);
}
switch (cmd) {
- case "enable": return setEnabled(true);
- case "disable": return setEnabled(false);
- default: return handleDefaultCommands(cmd);
+ case "enable":
+ return setEnabled(true);
+ case "disable":
+ return setEnabled(false);
+ default:
+ return handleDefaultCommands(cmd);
}
}
private int setEnabled(boolean enabled) {
- final var pw = getOutPrintWriter();
final var userId = UserHandle.parseUserArg(getNextArgRequired());
- mService.setSupervisionEnabledForUser(userId, enabled);
+ mService.mInternal.setSupervisionEnabledForUser(userId, enabled);
return 0;
}
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
index 1dd48f581bf4..06acb91509a1 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionUserData.java
@@ -26,6 +26,7 @@ import android.util.IndentingPrintWriter;
public class SupervisionUserData {
public final @UserIdInt int userId;
public boolean supervisionEnabled;
+ @Nullable public String supervisionAppPackage;
public boolean supervisionLockScreenEnabled;
@Nullable public PersistableBundle supervisionLockScreenOptions;
@@ -38,6 +39,7 @@ public class SupervisionUserData {
pw.println("User " + userId + ":");
pw.increaseIndent();
pw.println("supervisionEnabled: " + supervisionEnabled);
+ pw.println("supervisionAppPackage: " + supervisionAppPackage);
pw.println("supervisionLockScreenEnabled: " + supervisionLockScreenEnabled);
pw.println("supervisionLockScreenOptions: " + supervisionLockScreenOptions);
pw.decreaseIndent();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
index 01061f16c279..d9224eaf66ca 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginManagerTest.kt
@@ -29,6 +29,7 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
private val TEST_PLUGIN_TYPE = PluginType(Int::class.java, "test_type")
+private val DISPLAY_ID = "display_id"
@SmallTest
class PluginManagerTest {
@@ -62,18 +63,18 @@ class PluginManagerTest {
fun testSubscribe() {
val pluginManager = createPluginManager()
- pluginManager.subscribe(TEST_PLUGIN_TYPE, mockListener)
+ pluginManager.subscribe(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
- verify(testInjector.mockStorage).addListener(TEST_PLUGIN_TYPE, mockListener)
+ verify(testInjector.mockStorage).addListener(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
}
@Test
fun testUnsubscribe() {
val pluginManager = createPluginManager()
- pluginManager.unsubscribe(TEST_PLUGIN_TYPE, mockListener)
+ pluginManager.unsubscribe(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
- verify(testInjector.mockStorage).removeListener(TEST_PLUGIN_TYPE, mockListener)
+ verify(testInjector.mockStorage).removeListener(TEST_PLUGIN_TYPE, DISPLAY_ID, mockListener)
}
private fun createPluginManager(enabled: Boolean = true): PluginManager {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
index 218e34134e93..8eb3e9fbf9b0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/plugin/PluginStorageTest.kt
@@ -23,6 +23,8 @@ import org.junit.Test
private val TEST_PLUGIN_TYPE1 = PluginType(String::class.java, "test_type1")
private val TEST_PLUGIN_TYPE2 = PluginType(String::class.java, "test_type2")
+private val DISPLAY_ID_1 = "display_1"
+private val DISPLAY_ID_2 = "display_2"
@SmallTest
class PluginStorageTest {
@@ -33,9 +35,9 @@ class PluginStorageTest {
fun testUpdateValue() {
val type1Value = "value1"
val testChangeListener = TestPluginChangeListener<String>()
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
}
@@ -44,9 +46,9 @@ class PluginStorageTest {
fun testAddListener() {
val type1Value = "value1"
val testChangeListener = TestPluginChangeListener<String>()
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
}
@@ -55,10 +57,10 @@ class PluginStorageTest {
fun testRemoveListener() {
val type1Value = "value1"
val testChangeListener = TestPluginChangeListener<String>()
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
- storage.removeListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
+ storage.removeListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
assertThat(testChangeListener.receivedValue).isNull()
}
@@ -68,10 +70,10 @@ class PluginStorageTest {
val type1Value = "value1"
val type2Value = "value2"
val testChangeListener = TestPluginChangeListener<String>()
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
- storage.updateValue(TEST_PLUGIN_TYPE2, type2Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE2, DISPLAY_ID_1, type2Value)
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener)
assertThat(testChangeListener.receivedValue).isEqualTo(type1Value)
}
@@ -81,15 +83,62 @@ class PluginStorageTest {
val type1Value = "value1"
val testChangeListener1 = TestPluginChangeListener<String>()
val testChangeListener2 = TestPluginChangeListener<String>()
- storage.addListener(TEST_PLUGIN_TYPE1, testChangeListener1)
- storage.addListener(TEST_PLUGIN_TYPE2, testChangeListener2)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE2, DISPLAY_ID_1, testChangeListener2)
- storage.updateValue(TEST_PLUGIN_TYPE1, type1Value)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
assertThat(testChangeListener2.receivedValue).isNull()
}
+ @Test
+ fun testUpdateGlobal_noDisplaySpecificValue() {
+ val type1Value = "value1"
+ val testChangeListener1 = TestPluginChangeListener<String>()
+ val testChangeListener2 = TestPluginChangeListener<String>()
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+ storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1Value)
+
+ assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
+ assertThat(testChangeListener2.receivedValue).isEqualTo(type1Value)
+ }
+
+ @Test
+ fun testUpdateGlobal_withDisplaySpecificValue() {
+ val type1Value = "value1"
+ val type1GlobalValue = "value1Global"
+ val testChangeListener1 = TestPluginChangeListener<String>()
+ val testChangeListener2 = TestPluginChangeListener<String>()
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+ storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1GlobalValue)
+
+ assertThat(testChangeListener1.receivedValue).isEqualTo(type1Value)
+ assertThat(testChangeListener2.receivedValue).isEqualTo(type1GlobalValue)
+ }
+
+ @Test
+ fun testUpdateGlobal_withDisplaySpecificValueRemoved() {
+ val type1Value = "value1"
+ val type1GlobalValue = "value1Global"
+ val testChangeListener1 = TestPluginChangeListener<String>()
+ val testChangeListener2 = TestPluginChangeListener<String>()
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, testChangeListener1)
+ storage.addListener(TEST_PLUGIN_TYPE1, DISPLAY_ID_2, testChangeListener2)
+
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, type1Value)
+ storage.updateGlobalValue(TEST_PLUGIN_TYPE1, type1GlobalValue)
+ storage.updateValue(TEST_PLUGIN_TYPE1, DISPLAY_ID_1, null)
+
+ assertThat(testChangeListener1.receivedValue).isEqualTo(type1GlobalValue)
+ assertThat(testChangeListener2.receivedValue).isEqualTo(type1GlobalValue)
+ }
+
private class TestPluginChangeListener<T> : PluginChangeListener<T> {
var receivedValue: T? = null
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 8dc8c14f8948..cb52f1849b5b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -151,6 +151,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.flag.util.FlagSetException;
@@ -192,7 +193,9 @@ import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
+import org.mockito.InOrder;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
@@ -435,6 +438,7 @@ public final class AlarmManagerServiceTest {
private void disableFlagsNotSetByAnnotation() {
try {
mSetFlagsRule.disableFlags(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND);
} catch (FlagSetException fse) {
// Expected if the test about to be run requires this enabled.
}
@@ -948,6 +952,28 @@ public final class AlarmManagerServiceTest {
}
@Test
+ @EnableFlags(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND)
+ public void testWakelockOrdering() throws Exception {
+ final long triggerTime = mNowElapsedTest + 5000;
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
+
+ mNowElapsedTest = mTestTimer.getElapsed();
+ mTestTimer.expire();
+
+ final InOrder inOrder = Mockito.inOrder(alarmPi, mWakeLock);
+ inOrder.verify(mWakeLock).acquire();
+
+ final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
+ ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
+ inOrder.verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
+ onFinishedCaptor.capture(), any(Handler.class), isNull(), any());
+ onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null);
+
+ inOrder.verify(mWakeLock).release();
+ }
+
+ @Test
public void testMinFuturityCoreUid() {
setDeviceConfigLong(KEY_MIN_FUTURITY, 10L);
assertEquals(10, mService.mConstants.MIN_FUTURITY);
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 793ba7bc89bd..b92afc5c0ca7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -960,6 +960,74 @@ public class WallpaperManagerServiceTests {
.isEqualTo(FLAG_LOCK | FLAG_SYSTEM);
}
+ // Verify a secondary display removes system decorations started
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ public void displayRemoveSystemDecorations_sameSystemAndLockWallpaper_shouldDetachWallpaperServiceOnce()
+ throws Exception {
+ // GIVEN the same wallpaper used for the lock and system.
+ final int testUserId = USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ final WallpaperData wallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM, testUserId);
+ IWallpaperService mockIWallpaperService = mock(IWallpaperService.class);
+ wallpaper.connection.mService = mockIWallpaperService;
+ // GIVEN there are two displays: DEFAULT_DISPLAY, 2
+ final int testDisplayId = 2;
+ setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+ // GIVEN wallpaper connections have been established for displayID, 2.
+ WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
+ WallpaperManagerInternal.class);
+ wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ // Save displayConnector for displayId 2 before display removal.
+ WallpaperManagerService.DisplayConnector displayConnector =
+ wallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
+
+ // WHEN displayId, 2, is removed.
+ wallpaperManagerInternal.onDisplayRemoveSystemDecorations(testDisplayId);
+
+ // Then the wallpaper connection for displayId, 2, is detached.
+ verify(mockIWallpaperService).detach(eq(displayConnector.mToken));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ public void displayRemoveSystemDecorations_differentSystemAndLockWallpapers_shouldDetachWallpaperServiceTwice()
+ throws Exception {
+ // GIVEN different wallpapers used for the lock and system.
+ final int testUserId = USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
+ FLAG_LOCK, testUserId);
+ final WallpaperData systemWallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM,
+ testUserId);
+ final WallpaperData lockWallpaper = mService.getCurrentWallpaperData(FLAG_LOCK,
+ testUserId);
+ IWallpaperService mockIWallpaperService = mock(IWallpaperService.class);
+ systemWallpaper.connection.mService = mockIWallpaperService;
+ lockWallpaper.connection.mService = mockIWallpaperService;
+ // GIVEN there are two displays: DEFAULT_DISPLAY, 2
+ final int testDisplayId = 2;
+ setUpDisplays(List.of(DEFAULT_DISPLAY, testDisplayId));
+ // GIVEN wallpaper connections have been established for displayID, 2.
+ WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
+ WallpaperManagerInternal.class);
+ wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ // Save displayConnectors for displayId 2 before display removal.
+ WallpaperManagerService.DisplayConnector systemWallpaperDisplayConnector =
+ systemWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
+ WallpaperManagerService.DisplayConnector lockWallpaperDisplayConnector =
+ lockWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
+
+ // WHEN displayId, 2, is removed.
+ wallpaperManagerInternal.onDisplayRemoveSystemDecorations(testDisplayId);
+
+ // Then the system wallpaper connection for displayId, 2, is detached.
+ verify(mockIWallpaperService).detach(eq(systemWallpaperDisplayConnector.mToken));
+ // Then the lock wallpaper connection for displayId, 2, is detached.
+ verify(mockIWallpaperService).detach(eq(lockWallpaperDisplayConnector.mToken));
+ }
+ // Verify a secondary display removes system decorations ended
+
// Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
// non-current user must not bind to wallpaper service.
private void verifyNoConnectionBeforeLastUser(int lastUserId) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
index ecc48bfc40e4..f55ca0c0b12b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterTest.java
@@ -57,6 +57,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
+import com.android.server.accessibility.autoclick.AutoclickController;
import com.android.server.accessibility.gestures.TouchExplorer;
import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index acce813ff659..f02bdae1d9e6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.accessibility;
+package com.android.server.accessibility.autoclick;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -39,6 +39,8 @@ import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import com.android.server.accessibility.AccessibilityTraceManager;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
diff --git a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
index 8d3eef4a3168..cbe2bfb26cd6 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
@@ -28,8 +28,10 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
@@ -95,6 +97,10 @@ public class AbsoluteVolumeBehaviorTest {
when(mResources.getBoolean(com.android.internal.R.bool.config_useFixedVolume))
.thenReturn(false);
+ when(mContext.checkCallingOrSelfPermission(
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
mSystemServer = new NoOpSystemServerAdapter();
mSettingsAdapter = new NoOpSettingsAdapter();
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 3ced56a04138..a58a9cd2a28f 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -34,7 +34,6 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -53,15 +52,11 @@ import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
-import android.Manifest;
import android.annotation.SuppressLint;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AppOpsManager;
-import android.app.Instrumentation;
import android.app.KeyguardManager;
-import android.app.role.RoleManager;
-import android.companion.AssociationRequest;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -76,11 +71,9 @@ import android.media.projection.StopReason;
import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
-import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.TestLooper;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -99,7 +92,6 @@ import com.android.server.testutils.OffsettableClock;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -110,7 +102,6 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -292,8 +283,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(stoppedCallback2).isFalse();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked() throws Exception {
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
@@ -308,8 +297,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(mIMediaProjectionCallback.mLatch.await(5, TimeUnit.SECONDS)).isTrue();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_packageAllowlisted()
throws NameNotFoundException {
@@ -325,8 +312,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(mService.getActiveProjectionInfo()).isNotNull();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_AppOpMediaProjection()
throws NameNotFoundException {
@@ -347,50 +332,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(mService.getActiveProjectionInfo()).isNotNull();
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
- @Test
- public void testCreateProjection_keyguardLocked_RoleHeld() {
- runWithRole(
- AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- () -> {
- try {
- mAppInfo.privateFlags |= PRIVATE_FLAG_PRIVILEGED;
- doReturn(mAppInfo)
- .when(mPackageManager)
- .getApplicationInfoAsUser(
- anyString(),
- any(ApplicationInfoFlags.class),
- any(UserHandle.class));
- MediaProjectionManagerService.MediaProjection projection =
- mService.createProjectionInternal(
- Process.myUid(),
- mContext.getPackageName(),
- TYPE_MIRRORING,
- /* isPermanentGrant= */ false,
- UserHandle.CURRENT,
- DEFAULT_DISPLAY);
- doReturn(true).when(mKeyguardManager).isKeyguardLocked();
- doReturn(PackageManager.PERMISSION_DENIED)
- .when(mPackageManager)
- .checkPermission(RECORD_SENSITIVE_CONTENT, projection.packageName);
-
- projection.start(mIMediaProjectionCallback);
- projection.notifyVirtualDisplayCreated(10);
-
- // The projection was started because it was allowed to capture the
- // keyguard.
- assertWithMessage("Failed to run projection")
- .that(mService.getActiveProjectionInfo())
- .isNotNull();
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
- }
-
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_screenshareProtectionsDisabled()
throws NameNotFoundException {
@@ -416,8 +357,6 @@ public class MediaProjectionManagerServiceTest {
}
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testCreateProjection_keyguardLocked_noDisplayCreated()
throws NameNotFoundException {
@@ -509,8 +448,6 @@ public class MediaProjectionManagerServiceTest {
assertThat(secondProjection).isNotEqualTo(projection);
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testReuseProjection_keyguardNotLocked_startConsentDialog()
throws NameNotFoundException {
@@ -527,8 +464,6 @@ public class MediaProjectionManagerServiceTest {
verify(mContext).startActivityAsUser(any(), any());
}
- @EnableFlags(android.companion.virtualdevice.flags
- .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
@Test
public void testReuseProjection_keyguardLocked_noConsentDialog() throws NameNotFoundException {
MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
@@ -1302,48 +1237,6 @@ public class MediaProjectionManagerServiceTest {
return mService.getProjectionInternal(UID, PACKAGE_NAME);
}
- /**
- * Run the provided block giving the current context's package the provided role.
- */
- @SuppressWarnings("SameParameterValue")
- private void runWithRole(String role, Runnable block) {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- String packageName = mContext.getPackageName();
- UserHandle user = instrumentation.getTargetContext().getUser();
- RoleManager roleManager = Objects.requireNonNull(
- mContext.getSystemService(RoleManager.class));
- try {
- CountDownLatch latch = new CountDownLatch(1);
- instrumentation.getUiAutomation().adoptShellPermissionIdentity(
- Manifest.permission.MANAGE_ROLE_HOLDERS,
- Manifest.permission.BYPASS_ROLE_QUALIFICATION);
-
- roleManager.setBypassingRoleQualification(true);
- roleManager.addRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), success -> {
- if (success) {
- latch.countDown();
- } else {
- Assert.fail("Couldn't set role for test (failure) " + role);
- }
- });
- assertWithMessage("Couldn't set role for test (timeout) : " + role)
- .that(latch.await(1, TimeUnit.SECONDS)).isTrue();
- block.run();
-
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } finally {
- roleManager.removeRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), (aBool) -> {});
- roleManager.setBypassingRoleQualification(false);
- instrumentation.getUiAutomation()
- .dropShellPermissionIdentity();
- }
- }
-
private static class FakeIMediaProjectionCallback extends IMediaProjectionCallback.Stub {
CountDownLatch mLatch = new CountDownLatch(1);
@Override
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
index 379079a0018c..10ac0495d69a 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionStopControllerTest.java
@@ -22,7 +22,6 @@ import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -37,13 +36,10 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.Manifest;
import android.annotation.SuppressLint;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
-import android.app.Instrumentation;
import android.app.KeyguardManager;
-import android.app.role.RoleManager;
import android.companion.AssociationRequest;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -69,7 +65,6 @@ import com.android.server.SystemConfig;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -79,9 +74,7 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -123,6 +116,8 @@ public class MediaProjectionStopControllerTest {
private KeyguardManager mKeyguardManager;
@Mock
private TelecomManager mTelecomManager;
+ @Mock
+ private MediaProjectionStopController.RoleHolderProvider mRoleManager;
private AppOpsManager mAppOpsManager;
@Mock
@@ -145,7 +140,7 @@ public class MediaProjectionStopControllerTest {
mContext.addMockSystemService(TelecomManager.class, mTelecomManager);
mContext.setMockPackageManager(mPackageManager);
- mStopController = new MediaProjectionStopController(mContext, mStopConsumer);
+ mStopController = new MediaProjectionStopController(mContext, mStopConsumer, mRoleManager);
mService = new MediaProjectionManagerService(mContext,
mMediaProjectionMetricsLoggerInjector);
@@ -170,8 +165,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testMediaProjectionNotRestricted() throws Exception {
when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
@@ -180,8 +173,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testMediaProjectionRestricted() throws Exception {
MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
mediaProjection.notifyVirtualDisplayCreated(1);
@@ -239,21 +230,13 @@ public class MediaProjectionStopControllerTest {
@Test
public void testExemptFromStoppingHasAppStreamingRole() throws Exception {
- runWithRole(
- AssociationRequest.DEVICE_PROFILE_APP_STREAMING,
- () -> {
- try {
- MediaProjectionManagerService.MediaProjection mediaProjection =
- createMediaProjection();
- doReturn(PackageManager.PERMISSION_DENIED).when(
- mPackageManager).checkPermission(
- RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
- assertThat(mStopController.isExemptFromStopping(mediaProjection,
- MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ MediaProjectionManagerService.MediaProjection mediaProjection = createMediaProjection();
+ doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
+ RECORD_SENSITIVE_CONTENT, mediaProjection.packageName);
+ doReturn(List.of(mediaProjection.packageName)).when(mRoleManager).getRoleHoldersAsUser(
+ eq(AssociationRequest.DEVICE_PROFILE_APP_STREAMING), any(UserHandle.class));
+ assertThat(mStopController.isExemptFromStopping(mediaProjection,
+ MediaProjectionStopController.STOP_REASON_UNKNOWN)).isTrue();
}
@Test
@@ -316,8 +299,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testKeyguardLockedStateChanged_unlocked() {
mStopController.onKeyguardLockedStateChanged(false);
@@ -325,8 +306,6 @@ public class MediaProjectionStopControllerTest {
}
@Test
- @EnableFlags(
- android.companion.virtualdevice.flags.Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
public void testKeyguardLockedStateChanged_locked() {
mStopController.onKeyguardLockedStateChanged(true);
@@ -438,47 +417,4 @@ public class MediaProjectionStopControllerTest {
MediaProjectionManager.TYPE_SCREEN_CAPTURE, false, mContext.getUser(),
INVALID_DISPLAY);
}
-
- /**
- * Run the provided block giving the current context's package the provided role.
- */
- @SuppressWarnings("SameParameterValue")
- private void runWithRole(String role, Runnable block) {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- String packageName = mContext.getPackageName();
- UserHandle user = instrumentation.getTargetContext().getUser();
- RoleManager roleManager = Objects.requireNonNull(
- mContext.getSystemService(RoleManager.class));
- try {
- CountDownLatch latch = new CountDownLatch(1);
- instrumentation.getUiAutomation().adoptShellPermissionIdentity(
- Manifest.permission.MANAGE_ROLE_HOLDERS,
- Manifest.permission.BYPASS_ROLE_QUALIFICATION);
-
- roleManager.setBypassingRoleQualification(true);
- roleManager.addRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), success -> {
- if (success) {
- latch.countDown();
- } else {
- Assert.fail("Couldn't set role for test (failure) " + role);
- }
- });
- assertWithMessage("Couldn't set role for test (timeout) : " + role)
- .that(latch.await(1, TimeUnit.SECONDS)).isTrue();
- block.run();
-
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } finally {
- roleManager.removeRoleHolderAsUser(role, packageName,
- /* flags= */ RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP, user,
- mContext.getMainExecutor(), (aBool) -> {
- });
- roleManager.setBypassingRoleQualification(false);
- instrumentation.getUiAutomation()
- .dropShellPermissionIdentity();
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index 5862ac65eba9..af50effb7c8e 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -92,6 +92,21 @@ class SupervisionServiceTest {
simulateUserStarting(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(systemSupervisionPackage)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun onUserStarting_legacyProfileOwnerComponent_enablesSupervision() {
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(supervisionProfileOwnerComponent)
+
+ simulateUserStarting(USER_ID)
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(supervisionProfileOwnerComponent.packageName)
}
@Test
@@ -103,6 +118,7 @@ class SupervisionServiceTest {
simulateUserStarting(USER_ID, preCreated = true)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
}
@Test
@@ -114,6 +130,7 @@ class SupervisionServiceTest {
simulateUserStarting(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
}
@Test
@@ -125,6 +142,21 @@ class SupervisionServiceTest {
broadcastProfileOwnerChanged(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(systemSupervisionPackage)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun profileOwnerChanged_legacyProfileOwnerComponent_enablesSupervision() {
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(supervisionProfileOwnerComponent)
+
+ broadcastProfileOwnerChanged(USER_ID)
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID))
+ .isEqualTo(supervisionProfileOwnerComponent.packageName)
}
@Test
@@ -136,13 +168,14 @@ class SupervisionServiceTest {
broadcastProfileOwnerChanged(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ assertThat(service.getActiveSupervisionAppPackage(USER_ID)).isNull()
}
@Test
fun isActiveSupervisionApp_supervisionUid_supervisionEnabled_returnsTrue() {
whenever(mockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(arrayOf(systemSupervisionPackage))
- service.setSupervisionEnabledForUser(USER_ID, true)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isTrue()
}
@@ -151,7 +184,7 @@ class SupervisionServiceTest {
fun isActiveSupervisionApp_supervisionUid_supervisionNotEnabled_returnsFalse() {
whenever(mockPackageManager.getPackagesForUid(APP_UID))
.thenReturn(arrayOf(systemSupervisionPackage))
- service.setSupervisionEnabledForUser(USER_ID, false)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, false)
assertThat(service.mInternal.isActiveSupervisionApp(APP_UID)).isFalse()
}
@@ -167,15 +200,15 @@ class SupervisionServiceTest {
fun setSupervisionEnabledForUser() {
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
- service.setSupervisionEnabledForUser(USER_ID, true)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
- service.setSupervisionEnabledForUser(USER_ID, false)
+ service.mInternal.setSupervisionEnabledForUser(USER_ID, false)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
}
@Test
- fun supervisionEnabledForUser_internal() {
+ fun setSupervisionEnabledForUser_internal() {
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
service.mInternal.setSupervisionEnabledForUser(USER_ID, true)
@@ -205,6 +238,13 @@ class SupervisionServiceTest {
private val systemSupervisionPackage: String
get() = context.getResources().getString(R.string.config_systemSupervision)
+ private val supervisionProfileOwnerComponent: ComponentName
+ get() =
+ context
+ .getResources()
+ .getString(R.string.config_defaultSupervisionProfileOwnerComponent)
+ .let(ComponentName::unflattenFromString)!!
+
private fun simulateUserStarting(userId: Int, preCreated: Boolean = false) {
val userInfo = UserInfo(userId, /* name= */ "tempUser", /* flags= */ 0)
userInfo.preCreated = preCreated
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 5ac3e483231c..7af4ede05363 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4461,7 +4461,46 @@ public class SizeCompatTests extends WindowTestsBase {
// are aligned to the top of the parentAppBounds
assertEquals(new Rect(0, notchHeight, 1000, 1200), appBounds);
assertEquals(new Rect(0, 0, 1000, 1200), bounds);
+ }
+
+ @Test
+ @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
+ public void testInFreeform_boundsSandboxedToAppBounds() {
+ final int dw = 2800;
+ final int dh = 1400;
+ final int notchHeight = 100;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, dw, dh)
+ .setNotch(notchHeight)
+ .build();
+ setUpApp(display);
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+ mTask.mDisplayContent.getDefaultTaskDisplayArea()
+ .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ Rect appBounds = new Rect(0, 0, 1000, 500);
+ Rect bounds = new Rect(0, 0, 1000, 600);
+ mTask.getWindowConfiguration().setAppBounds(appBounds);
+ mTask.getWindowConfiguration().setBounds(bounds);
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+
+ // Bounds are sandboxed to appBounds in freeform.
+ assertDownScaled();
+ assertEquals(mActivity.getWindowConfiguration().getAppBounds(),
+ mActivity.getWindowConfiguration().getBounds());
+
+ // Exit freeform.
+ mTask.mDisplayContent.getDefaultTaskDisplayArea()
+ .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mTask.getWindowConfiguration().setBounds(new Rect(0, 0, dw, dh));
+ mActivity.onConfigurationChanged(mTask.getConfiguration());
+ assertFitted();
+ appBounds = mActivity.getWindowConfiguration().getAppBounds();
+ bounds = mActivity.getWindowConfiguration().getBounds();
+ // Bounds are not sandboxed to appBounds.
+ assertNotEquals(appBounds, bounds);
+ assertEquals(notchHeight, appBounds.top - bounds.top);
}
@Test
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
index 9d9cac9702bb..d62fd63aeda6 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
@@ -22,8 +22,10 @@ import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Rlog;
import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -220,7 +222,7 @@ public final class SatelliteSubscriberInfo implements Parcelable {
StringBuilder sb = new StringBuilder();
sb.append("SubscriberId:");
- sb.append(mSubscriberId);
+ sb.append(Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mSubscriberId));
sb.append(",");
sb.append("CarrierId:");
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
index fb4f89ded547..75d3ec6d3fe6 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
@@ -21,8 +21,10 @@ import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Rlog;
import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.Objects;
@@ -132,7 +134,7 @@ public final class SatelliteSubscriberProvisionStatus implements Parcelable {
StringBuilder sb = new StringBuilder();
sb.append("SatelliteSubscriberInfo:");
- sb.append(mSubscriberInfo);
+ sb.append(Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mSubscriberInfo));
sb.append(",");
sb.append("ProvisionStatus:");