summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/boot-image-profile-extra.txt21
-rw-r--r--core/java/android/app/IUiModeManager.aidl54
-rw-r--r--core/java/android/app/IUiModeManagerCallback.aidl1
-rw-r--r--core/java/android/app/Notification.java12
-rw-r--r--core/java/android/app/UiModeManager.java137
-rw-r--r--core/java/android/os/ChildZygoteProcess.java3
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java62
-rw-r--r--core/java/android/os/Looper.java8
-rw-r--r--core/java/android/os/Message.java16
-rw-r--r--core/java/android/os/PerfettoTrace.java41
-rw-r--r--core/java/android/os/PerfettoTrackEventExtra.java20
-rw-r--r--core/java/android/os/TestLooperManager.java28
-rw-r--r--core/java/android/os/Trace.java1
-rw-r--r--core/java/android/view/ViewRootImpl.java69
-rw-r--r--core/java/android/view/inputmethod/ImeTracker.java1
-rw-r--r--core/java/android/window/DesktopExperienceFlags.java12
-rw-r--r--core/java/android/window/DesktopModeFlags.java94
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig21
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--core/res/res/drawable/accessibility_autoclick_button_rounded_background.xml20
-rw-r--r--core/res/res/drawable/accessibility_autoclick_left_click.xml25
-rw-r--r--core/res/res/drawable/accessibility_autoclick_pause.xml26
-rw-r--r--core/res/res/drawable/accessibility_autoclick_position.xml26
-rw-r--r--core/res/res/drawable/accessibility_autoclick_type_panel_rounded_background.xml20
-rw-r--r--core/res/res/layout/accessibility_autoclick_type_panel.xml78
-rw-r--r--core/res/res/layout/notification_2025_template_compact_heads_up_base.xml89
-rw-r--r--core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml116
-rw-r--r--core/res/res/values/dimens.xml12
-rw-r--r--core/res/res/values/strings.xml10
-rw-r--r--core/res/res/values/styles.xml16
-rw-r--r--core/res/res/values/symbols.xml20
-rw-r--r--core/tests/coretests/src/android/os/PerfettoTraceTest.java87
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java115
-rw-r--r--core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java11
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt1
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt1
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt2
-rw-r--r--libs/WindowManager/Shell/shared/Android.bp1
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DeviceConfig.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DeviceConfig.kt)2
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java159
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java78
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java57
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java7
-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/HandleMenu.kt1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt2
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt23
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppWithExternalDisplayConnectedTest.kt27
-rw-r--r--libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppWithExternalDisplayConnected.kt103
-rw-r--r--libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/ExtendedDisplaySettingsSession.kt34
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java204
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt56
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt47
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java19
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt63
-rw-r--r--libs/androidfw/Android.bp1
-rw-r--r--libs/androidfw/LocaleDataLookup.cpp7
-rw-r--r--libs/androidfw/TypeWrappers.cpp17
-rw-r--r--libs/androidfw/include/androidfw/TypeWrappers.h13
-rw-r--r--libs/androidfw/tests/LocaleDataLookup_bench.cpp57
-rw-r--r--libs/androidfw/tests/TypeWrappers_test.cpp12
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig7
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt63
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dump/DumpHandlerTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProvider.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderContentProviderTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt2
-rw-r--r--ravenwood/texts/ravenwood-annotation-allowed-classes.txt4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java84
-rw-r--r--services/core/java/com/android/server/BootReceiver.java7
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java1
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java131
-rw-r--r--services/core/java/com/android/server/ZramMaintenance.java100
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java22
-rw-r--r--services/core/java/com/android/server/input/KeyGestureController.java2
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java156
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java11
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerSettings.java26
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerShellCommand.java22
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java112
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java5
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java16
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java148
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java121
-rw-r--r--services/tests/servicestests/src/com/android/server/zram/ZramMaintenanceTest.kt155
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java4
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt4
-rwxr-xr-xtools/localedata/extract_icu_data.py9
161 files changed, 3052 insertions, 768 deletions
diff --git a/boot/boot-image-profile-extra.txt b/boot/boot-image-profile-extra.txt
index fd51f9cbcee1..ced0d176f174 100644
--- a/boot/boot-image-profile-extra.txt
+++ b/boot/boot-image-profile-extra.txt
@@ -45,3 +45,24 @@ HSPLandroid/os/MessageQueue$OnFileDescriptorEventListener;->*
HSPLandroid/os/MessageQueue$StackNodeType;->*
HSPLandroid/os/MessageQueue$StateNode;->*
HSPLandroid/os/MessageQueue$TimedParkStateNode;->*
+HSPLandroid/os/PerfettoTrace$Category;->*
+HSPLandroid/os/PerfettoTrace;->*
+HSPLandroid/os/PerfettoTrackEventExtra;->*
+HSPLandroid/os/PerfettoTrackEventExtra$BuilderImpl;->*
+HSPLandroid/os/PerfettoTrackEventExtra$NoOpBuilder;->*
+HSPLandroid/os/PerfettoTrackEventExtra$ArgBool;->*
+HSPLandroid/os/PerfettoTrackEventExtra$ArgInt64;->*
+HSPLandroid/os/PerfettoTrackEventExtra$ArgDouble;->*
+HSPLandroid/os/PerfettoTrackEventExtra$ArgString;->*
+HSPLandroid/os/PerfettoTrackEventExtra$CounterInt64;->*
+HSPLandroid/os/PerfettoTrackEventExtra$CounterDouble;->*
+HSPLandroid/os/PerfettoTrackEventExtra$CounterTrack;->*
+HSPLandroid/os/PerfettoTrackEventExtra$NamedTrack;->*
+HSPLandroid/os/PerfettoTrackEventExtra$Flow;->*
+HSPLandroid/os/PerfettoTrackEventExtra$Proto;->*
+HSPLandroid/os/PerfettoTrackEventExtra$FieldInt64;->*
+HSPLandroid/os/PerfettoTrackEventExtra$FieldDouble;->*
+HSPLandroid/os/PerfettoTrackEventExtra$FieldString;->*
+HSPLandroid/os/PerfettoTrackEventExtra$FieldNested;->*
+HSPLandroid/os/PerfettoTrackEventExtra$Pool;->*
+HSPLandroid/os/PerfettoTrackEventExtra$RingBuffer;->*
diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl
index 3b83024d536b..6d4ad7518546 100644
--- a/core/java/android/app/IUiModeManager.aidl
+++ b/core/java/android/app/IUiModeManager.aidl
@@ -51,7 +51,7 @@ interface IUiModeManager {
* Return the current running mode.
*/
int getCurrentModeType();
-
+
/**
* Sets the night mode.
* <p>
@@ -161,61 +161,69 @@ interface IUiModeManager {
boolean setNightModeActivated(boolean active);
/**
- * Returns custom start clock time
- */
+ * Returns custom start clock time
+ */
long getCustomNightModeStart();
/**
- * Sets custom start clock time
- */
+ * Sets custom start clock time
+ */
void setCustomNightModeStart(long time);
/**
- * Returns custom end clock time
- */
+ * Returns custom end clock time
+ */
long getCustomNightModeEnd();
/**
- * Sets custom end clock time
- */
+ * Sets custom end clock time
+ */
void setCustomNightModeEnd(long time);
/**
- * Sets projection state for the caller for the given projection type.
- */
+ * Sets projection state for the caller for the given projection type.
+ */
boolean requestProjection(in IBinder binder, int projectionType, String callingPackage);
/**
- * Releases projection state for the caller for the given projection type.
- */
+ * Releases projection state for the caller for the given projection type.
+ */
boolean releaseProjection(int projectionType, String callingPackage);
/**
- * Registers a listener for changes to projection state.
- */
+ * Registers a listener for changes to projection state.
+ */
@EnforcePermission("READ_PROJECTION_STATE")
void addOnProjectionStateChangedListener(in IOnProjectionStateChangedListener listener, int projectionType);
/**
- * Unregisters a listener for changes to projection state.
- */
+ * Unregisters a listener for changes to projection state.
+ */
@EnforcePermission("READ_PROJECTION_STATE")
void removeOnProjectionStateChangedListener(in IOnProjectionStateChangedListener listener);
/**
- * Returns packages that have currently set the given projection type.
- */
+ * Returns packages that have currently set the given projection type.
+ */
@EnforcePermission("READ_PROJECTION_STATE")
List<String> getProjectingPackages(int projectionType);
/**
- * Returns currently set projection types.
- */
+ * Returns currently set projection types.
+ */
@EnforcePermission("READ_PROJECTION_STATE")
int getActiveProjectionTypes();
/**
- * Returns the contrast for the current user
- */
+ * Returns the contrast for the current user.
+ */
float getContrast();
+
+
+ /**
+ * Returns the force invert state.
+ *
+ * @hide
+ */
+ int getForceInvertState();
}
diff --git a/core/java/android/app/IUiModeManagerCallback.aidl b/core/java/android/app/IUiModeManagerCallback.aidl
index 47c18a8bd3cb..550779d14bab 100644
--- a/core/java/android/app/IUiModeManagerCallback.aidl
+++ b/core/java/android/app/IUiModeManagerCallback.aidl
@@ -24,4 +24,5 @@ package android.app;
*/
oneway interface IUiModeManagerCallback {
void notifyContrastChanged(float contrast);
+ void notifyForceInvertStateChanged(int forceInvertState);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c6de9a37f168..a6338fbd6b77 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -7599,11 +7599,19 @@ public class Notification implements Parcelable
}
private int getCompactHeadsUpBaseLayoutResource() {
- return R.layout.notification_template_material_compact_heads_up_base;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_compact_heads_up_base;
+ } else {
+ return R.layout.notification_template_material_compact_heads_up_base;
+ }
}
private int getMessagingCompactHeadsUpLayoutResource() {
- return R.layout.notification_template_material_messaging_compact_heads_up;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_compact_heads_up_messaging;
+ } else {
+ return R.layout.notification_template_material_messaging_compact_heads_up;
+ }
}
private int getExpandedBaseLayoutResource() {
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 57549847f05d..f6c789d51aee 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -121,6 +121,24 @@ public class UiModeManager {
}
/**
+ * Listener for the force invert state. To listen for changes to
+ * the force invert state on the device, implement this interface and
+ * register it with the system by calling {@link #addForceInvertStateChangeListener}.
+ *
+ * @hide
+ */
+ public interface ForceInvertStateChangeListener {
+
+ /**
+ * Called when the force invert state changes.
+ *
+ * @param forceInvertState The force invert state in {@link #getForceInvertState}
+ * @hide
+ */
+ void onForceInvertStateChanged(@ForceInvertType int forceInvertState);
+ }
+
+ /**
* Broadcast sent when the device's UI has switched to car mode, either
* by being placed in a car dock or explicit action of the user. After
* sending the broadcast, the system will start the intent
@@ -374,6 +392,36 @@ public class UiModeManager {
@SystemApi
public static final int MODE_NIGHT_CUSTOM_TYPE_BEDTIME = 1;
+ /** @hide */
+ @IntDef(prefix = {"Force_Invert_Type_"}, value = {
+ FORCE_INVERT_TYPE_OFF,
+ FORCE_INVERT_TYPE_DARK,
+ FORCE_INVERT_TYPE_LIGHT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ForceInvertType {}
+
+ /**
+ * Constant for {@link #getForceInvertState()}: Do not force invert.
+ *
+ * @hide
+ */
+ public static final int FORCE_INVERT_TYPE_OFF = 0;
+
+ /**
+ * Constant for {@link #getForceInvertState()}: Force apps to be dark.
+ *
+ * @hide
+ */
+ public static final int FORCE_INVERT_TYPE_DARK = 1;
+
+ /**
+ * Constant for {@link #getForceInvertState()}: Force apps to be light.
+ *
+ * @hide
+ */
+ public static final int FORCE_INVERT_TYPE_LIGHT = 2;
+
private static Globals sGlobals;
/**
@@ -405,6 +453,8 @@ public class UiModeManager {
private final IUiModeManager mService;
private final Object mGlobalsLock = new Object();
+ @ForceInvertType
+ private int mForceInvertState = FORCE_INVERT_TYPE_OFF;
private float mContrast = ContrastUtils.CONTRAST_DEFAULT_VALUE;
/**
@@ -414,16 +464,63 @@ public class UiModeManager {
private final ArrayMap<ContrastChangeListener, Executor>
mContrastChangeListeners = new ArrayMap<>();
+ private final ArrayMap<ForceInvertStateChangeListener, Executor>
+ mForceInvertStateChangeListeners = new ArrayMap<>();
+
Globals(IUiModeManager service) {
mService = service;
try {
mService.addCallback(this);
mContrast = mService.getContrast();
+ mForceInvertState = mService.getForceInvertState();
} catch (RemoteException e) {
Log.e(TAG, "Setup failed: UiModeManagerService is dead", e);
}
}
+ @ForceInvertType
+ private int getForceInvertState() {
+ synchronized (mGlobalsLock) {
+ return mForceInvertState;
+ }
+ }
+
+ private void addForceInvertStateChangeListener(ForceInvertStateChangeListener listener,
+ Executor executor) {
+ synchronized (mGlobalsLock) {
+ mForceInvertStateChangeListeners.put(listener, executor);
+ }
+ }
+
+ private void removeForceInvertStateChangeListener(ForceInvertStateChangeListener listener) {
+ synchronized (mGlobalsLock) {
+ mForceInvertStateChangeListeners.remove(listener);
+ }
+ }
+
+ @Override
+ public void notifyForceInvertStateChanged(@ForceInvertType int forceInvertState) {
+ final Map<ForceInvertStateChangeListener, Executor> listeners = new ArrayMap<>();
+ synchronized (mGlobalsLock) {
+ // if value changed in the settings, update the cached value and notify listeners
+ if (mForceInvertState == forceInvertState) {
+ return;
+ }
+
+ mForceInvertState = forceInvertState;
+ listeners.putAll(mForceInvertStateChangeListeners);
+ }
+
+ listeners.forEach((listener, executor) -> {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> listener.onForceInvertStateChanged(forceInvertState));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ });
+ }
+
private float getContrast() {
synchronized (mGlobalsLock) {
return mContrast;
@@ -1453,4 +1550,44 @@ public class UiModeManager {
Objects.requireNonNull(listener);
sGlobals.removeContrastChangeListener(listener);
}
+
+ /**
+ * Returns the force invert state for the user.
+ *
+ * @hide
+ */
+ @ForceInvertType
+ public int getForceInvertState() {
+ return sGlobals.getForceInvertState();
+ }
+
+ /**
+ * Registers a {@link ForceInvertStateChangeListener} for the current user.
+ *
+ * @param executor The executor on which the listener should be called back.
+ * @param listener The listener.
+ *
+ * @hide
+ */
+ public void addForceInvertStateChangeListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull ForceInvertStateChangeListener listener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ sGlobals.addForceInvertStateChangeListener(listener, executor);
+ }
+
+ /**
+ * Unregisters a {@link ForceInvertStateChangeListener} for the current user.
+ * If the listener was not registered, does nothing and returns.
+ *
+ * @param listener The listener to unregister.
+ *
+ * @hide
+ */
+ public void removeForceInvertStateChangeListener(
+ @NonNull ForceInvertStateChangeListener listener) {
+ Objects.requireNonNull(listener);
+ sGlobals.removeForceInvertStateChangeListener(listener);
+ }
}
diff --git a/core/java/android/os/ChildZygoteProcess.java b/core/java/android/os/ChildZygoteProcess.java
index d8f825a2ee60..84fd0ca4cce9 100644
--- a/core/java/android/os/ChildZygoteProcess.java
+++ b/core/java/android/os/ChildZygoteProcess.java
@@ -67,12 +67,15 @@ public class ChildZygoteProcess extends ZygoteProcess {
if (mDead.get()) {
return true;
}
+ StrictMode.ThreadPolicy oldStrictModeThreadPolicy = StrictMode.allowThreadDiskReads();
try {
if (Os.stat("/proc/" + mPid).st_uid == mUid) {
return false;
}
} catch (ErrnoException e) {
// Do nothing, it's dead.
+ } finally {
+ StrictMode.setThreadPolicy(oldStrictModeThreadPolicy);
}
mDead.set(true);
return true;
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 877f130a8b5a..349a2f0a181d 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -24,8 +24,6 @@ import android.annotation.TestApi;
import android.app.ActivityThread;
import android.app.Instrumentation;
import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Process;
-import android.os.UserHandle;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodRedirect;
import android.ravenwood.annotation.RavenwoodRedirectionClass;
@@ -95,6 +93,13 @@ public final class MessageQueue {
private int mAsyncMessageCount;
/**
+ * @hide
+ */
+ private final AtomicLong mMessageCount = new AtomicLong();
+ private final Thread mThread;
+ private final long mTid;
+
+ /**
* Select between two implementations of message queue. The legacy implementation is used
* by default as it provides maximum compatibility with applications and tests that
* reach into MessageQueue via the mMessages field. The concurrent implemmentation is used for
@@ -128,6 +133,8 @@ public final class MessageQueue {
mUseConcurrent = sIsProcessAllowedToUseConcurrent && !isInstrumenting();
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
+ mThread = Thread.currentThread();
+ mTid = Process.myTid();
}
private static void initIsProcessAllowedToUseConcurrent() {
@@ -218,6 +225,32 @@ public final class MessageQueue {
}
}
+ private void decAndTraceMessageCount() {
+ mMessageCount.decrementAndGet();
+ traceMessageCount();
+ }
+
+ private void incAndTraceMessageCount(Message msg, long when) {
+ mMessageCount.incrementAndGet();
+ msg.mSendingThreadName = Thread.currentThread().getName();
+ msg.mEventId.set(PerfettoTrace.getFlowId());
+
+ traceMessageCount();
+ PerfettoTrace.instant(PerfettoTrace.MQ_CATEGORY, "message_queue_send")
+ .addFlow(msg.mEventId.get())
+ .addArg("receiving_thread", mThread.getName())
+ .addArg("delay", when - SystemClock.uptimeMillis())
+ .addArg("what", msg.what)
+ .emit();
+ }
+
+ /** @hide */
+ private void traceMessageCount() {
+ PerfettoTrace.counter(PerfettoTrace.MQ_CATEGORY, mMessageCount.get())
+ .usingThreadCounterTrack(mTid, mThread.getName())
+ .emit();
+ }
+
// Disposes of the underlying message queue.
// Must only be called on the looper thread or the finalizer.
private void dispose() {
@@ -800,6 +833,7 @@ public final class MessageQueue {
Message msg = nextMessage(false, false);
if (msg != null) {
msg.markInUse();
+ decAndTraceMessageCount();
return msg;
}
@@ -909,6 +943,7 @@ public final class MessageQueue {
if (msg.isAsynchronous()) {
mAsyncMessageCount--;
}
+ decAndTraceMessageCount();
if (TRACE) {
Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
}
@@ -1075,6 +1110,7 @@ public final class MessageQueue {
msg.markInUse();
msg.arg1 = token;
+ incAndTraceMessageCount(msg, when);
if (!enqueueMessageUnchecked(msg, when)) {
Log.wtf(TAG_C, "Unexpected error while adding sync barrier!");
@@ -1090,6 +1126,7 @@ public final class MessageQueue {
msg.markInUse();
msg.when = when;
msg.arg1 = token;
+ incAndTraceMessageCount(msg, when);
if (Flags.messageQueueTailTracking() && mLast != null && mLast.when <= when) {
/* Message goes to tail of list */
@@ -1196,6 +1233,7 @@ public final class MessageQueue {
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
+ decAndTraceMessageCount();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
@@ -1252,6 +1290,8 @@ public final class MessageQueue {
msg.markInUse();
msg.when = when;
+ incAndTraceMessageCount(msg, when);
+
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
@@ -1391,6 +1431,7 @@ public final class MessageQueue {
if (msg.isAsynchronous()) {
mAsyncMessageCount--;
}
+ decAndTraceMessageCount();
if (TRACE) {
Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
}
@@ -1642,6 +1683,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
p.recycleUnchecked();
+ decAndTraceMessageCount();
p = n;
}
@@ -1660,6 +1702,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
n.recycleUnchecked();
+ decAndTraceMessageCount();
p.next = nn;
if (p.next == null) {
mLast = p;
@@ -1718,6 +1761,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
n.recycleUnchecked();
+ decAndTraceMessageCount();
p.next = nn;
if (p.next == null) {
mLast = p;
@@ -1759,6 +1803,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
p.recycleUnchecked();
+ decAndTraceMessageCount();
p = n;
}
@@ -1777,6 +1822,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
n.recycleUnchecked();
+ decAndTraceMessageCount();
p.next = nn;
if (p.next == null) {
mLast = p;
@@ -1832,6 +1878,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
p.recycleUnchecked();
+ decAndTraceMessageCount();
p = n;
}
@@ -1850,6 +1897,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
n.recycleUnchecked();
+ decAndTraceMessageCount();
p.next = nn;
if (p.next == null) {
mLast = p;
@@ -1904,6 +1952,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
p.recycleUnchecked();
+ decAndTraceMessageCount();
p = n;
}
@@ -1921,6 +1970,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
n.recycleUnchecked();
+ decAndTraceMessageCount();
p.next = nn;
if (p.next == null) {
mLast = p;
@@ -1976,6 +2026,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
p.recycleUnchecked();
+ decAndTraceMessageCount();
p = n;
}
@@ -1993,6 +2044,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
n.recycleUnchecked();
+ decAndTraceMessageCount();
p.next = nn;
if (p.next == null) {
mLast = p;
@@ -2027,6 +2079,8 @@ public final class MessageQueue {
mMessages = null;
mLast = null;
mAsyncMessageCount = 0;
+ mMessageCount.set(0);
+ traceMessageCount();
}
private void removeAllFutureMessagesLocked() {
@@ -2057,6 +2111,7 @@ public final class MessageQueue {
mAsyncMessageCount--;
}
p.recycleUnchecked();
+ decAndTraceMessageCount();
} while (n != null);
}
}
@@ -2701,6 +2756,7 @@ public final class MessageQueue {
MessageNode node = new MessageNode(msg, seq);
msg.when = when;
msg.markInUse();
+ incAndTraceMessageCount(msg, when);
if (DEBUG) {
Log.d(TAG_C, "Insert message what: " + msg.what + " when: " + msg.when + " seq: "
@@ -2828,6 +2884,7 @@ public final class MessageQueue {
if (removeMatches) {
if (p.removeFromStack()) {
p.mMessage.recycleUnchecked();
+ decAndTraceMessageCount();
if (mMessageCounts.incrementCancelled()) {
nativeWake(mPtr);
}
@@ -2870,6 +2927,7 @@ public final class MessageQueue {
found = true;
if (queue.remove(msg)) {
msg.mMessage.recycleUnchecked();
+ decAndTraceMessageCount();
}
} else {
return true;
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 2fe4871e08dd..d16e4473d55f 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -199,7 +199,12 @@ public final class Looper {
return false;
}
- // This must be in a local variable, in case a UI event sets the logger
+ PerfettoTrace.begin(PerfettoTrace.MQ_CATEGORY, "message_queue_receive")
+ .addArg("sending_thread", msg.mSendingThreadName)
+ .addTerminatingFlow(msg.mEventId.get())
+ .emit();
+
+ // This must be in a local variabe, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
@@ -289,6 +294,7 @@ public final class Looper {
+ msg.callback + " what=" + msg.what);
}
+ PerfettoTrace.end(PerfettoTrace.MQ_CATEGORY).emit();
msg.recycleUnchecked();
return true;
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 702fdc2bbaa6..b22d1774d967 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -23,6 +23,8 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
*
* Defines a message containing a description and arbitrary data object that can be
@@ -37,6 +39,13 @@ import com.android.internal.annotations.VisibleForTesting;
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Message implements Parcelable {
/**
+ * For tracing
+ *
+ * @hide Only for use within the system server.
+ */
+ public final AtomicInteger mEventId = new AtomicInteger();
+
+ /**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
@@ -101,6 +110,13 @@ public final class Message implements Parcelable {
*/
public int workSourceUid = UID_NONE;
+ /**
+ * Sending thread
+ *
+ * @hide
+ */
+ public String mSendingThreadName;
+
/** If set message is in use.
* This flag is set when the message is enqueued and remains set while it
* is delivered and afterwards when it is recycled. The flag is only cleared
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
index 68f1570154ff..741d542ecb3b 100644
--- a/core/java/android/os/PerfettoTrace.java
+++ b/core/java/android/os/PerfettoTrace.java
@@ -16,6 +16,8 @@
package android.os;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
+
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -32,6 +34,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PerfettoTrace {
private static final String TAG = "PerfettoTrace";
@@ -48,11 +51,14 @@ public final class PerfettoTrace {
*/
private static final AtomicInteger sFlowEventId = new AtomicInteger();
+ public static final PerfettoTrace.Category MQ_CATEGORY = new PerfettoTrace.Category("mq");
+
/**
* Perfetto category a trace event belongs to.
* Registering a category is not sufficient to capture events within the category, it must
* also be enabled in the trace config.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static final class Category implements PerfettoTrackEventExtra.PerfettoPointer {
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
@@ -97,12 +103,16 @@ public final class PerfettoTrace {
mSeverity = severity;
mPtr = native_init(name, tag, severity);
mExtraPtr = native_get_extra_ptr(mPtr);
- sRegistry.registerNativeAllocation(this, mPtr);
+ if (!RavenwoodEnvironment.getInstance().isRunningOnRavenwood()) {
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
}
@FastNative
+ @android.ravenwood.annotation.RavenwoodReplace
private static native long native_init(String name, String tag, String severity);
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
private static native long native_delete();
@CriticalNative
private static native void native_register(long ptr);
@@ -111,8 +121,24 @@ public final class PerfettoTrace {
@CriticalNative
private static native boolean native_is_enabled(long ptr);
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
private static native long native_get_extra_ptr(long ptr);
+ private static long native_init$ravenwood(String name, String tag, String severity) {
+ // Tracing currently completely disabled under Ravenwood
+ return 0;
+ }
+
+ private static long native_delete$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return 0;
+ }
+
+ private static long native_get_extra_ptr$ravenwood(long ptr) {
+ // Tracing currently completely disabled under Ravenwood
+ return 0;
+ }
+
/**
* Register the category.
*/
@@ -134,10 +160,16 @@ public final class PerfettoTrace {
/**
* Whether the category is enabled or not.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public boolean isEnabled() {
return IS_FLAG_ENABLED && native_is_enabled(mPtr);
}
+ public boolean isEnabled$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return false;
+ }
+
/**
* Whether the category is registered or not.
*/
@@ -340,4 +372,11 @@ public final class PerfettoTrace {
public static void register(boolean isBackendInProcess) {
native_register(isBackendInProcess);
}
+
+ /**
+ * Registers categories with Perfetto.
+ */
+ public static void registerCategories() {
+ MQ_CATEGORY.register();
+ }
}
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
index e034fb3726e3..72b909e64c70 100644
--- a/core/java/android/os/PerfettoTrackEventExtra.java
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -16,6 +16,8 @@
package android.os;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
+
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -29,6 +31,7 @@ import java.util.function.Supplier;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PerfettoTrackEventExtra {
private static final int DEFAULT_EXTRA_CACHE_SIZE = 5;
private static final Builder NO_OP_BUILDER = new NoOpBuilder();
@@ -305,6 +308,7 @@ public final class PerfettoTrackEventExtra {
Builder endNested();
}
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static final class NoOpBuilder implements Builder {
@Override
public void emit() {}
@@ -759,7 +763,9 @@ public final class PerfettoTrackEventExtra {
private PerfettoTrackEventExtra() {
mPtr = native_init();
- sRegistry.registerNativeAllocation(this, mPtr);
+ if (!RavenwoodEnvironment.getInstance().isRunningOnRavenwood()) {
+ sRegistry.registerNativeAllocation(this, mPtr);
+ }
}
/**
@@ -1342,8 +1348,10 @@ public final class PerfettoTrackEventExtra {
}
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
private static native long native_init();
@CriticalNative
+ @android.ravenwood.annotation.RavenwoodReplace
private static native long native_delete();
@CriticalNative
private static native void native_add_arg(long ptr, long extraPtr);
@@ -1351,4 +1359,14 @@ public final class PerfettoTrackEventExtra {
private static native void native_clear_args(long ptr);
@FastNative
private static native void native_emit(int type, long tag, String name, long ptr);
+
+ private static long native_init$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return 0;
+ }
+
+ private static long native_delete$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return 0;
+ }
}
diff --git a/core/java/android/os/TestLooperManager.java b/core/java/android/os/TestLooperManager.java
index ddfa3799706e..82bdb2280c35 100644
--- a/core/java/android/os/TestLooperManager.java
+++ b/core/java/android/os/TestLooperManager.java
@@ -18,6 +18,7 @@ import android.annotation.FlaggedApi;
import android.annotation.Nullable;
import android.util.ArraySet;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
/**
@@ -38,10 +39,13 @@ public class TestLooperManager {
private final MessageQueue mQueue;
private final Looper mLooper;
private final LinkedBlockingQueue<MessageExecution> mExecuteQueue = new LinkedBlockingQueue<>();
+ private final boolean mLooperIsMyLooper;
+
+ // When this latch is zero, it's guaranteed that the LooperHolder Message
+ // is not in the underlying queue.
+ private final CountDownLatch mLooperHolderLatch = new CountDownLatch(1);
private boolean mReleased;
- private boolean mLooperBlocked;
- private final boolean mLooperIsMyLooper;
/**
* @hide
@@ -59,6 +63,8 @@ public class TestLooperManager {
if (!mLooperIsMyLooper) {
// Post a message that will keep the looper blocked as long as we are dispatching.
new Handler(looper).post(new LooperHolder());
+ } else {
+ mLooperHolderLatch.countDown();
}
}
@@ -222,23 +228,16 @@ public class TestLooperManager {
* is not in the underlying queue.
*/
private void waitForLooperHolder() {
- while (!mLooperIsMyLooper && !mLooperBlocked) {
- synchronized (this) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
+ try {
+ mLooperHolderLatch.await();
+ } catch (InterruptedException e) {
}
}
private class LooperHolder implements Runnable {
@Override
public void run() {
- synchronized (TestLooperManager.this) {
- mLooperBlocked = true;
- TestLooperManager.this.notify();
- }
+ mLooperHolderLatch.countDown();
while (!mReleased) {
try {
final MessageExecution take = mExecuteQueue.take();
@@ -248,9 +247,6 @@ public class TestLooperManager {
} catch (InterruptedException e) {
}
}
- synchronized (TestLooperManager.this) {
- mLooperBlocked = false;
- }
}
private void processMessage(MessageExecution mex) {
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 09e6a45dc294..bf4c4d16fe19 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -544,5 +544,6 @@ public final class Trace {
*/
public static void registerWithPerfetto() {
PerfettoTrace.register(false /* isBackendInProcess */);
+ PerfettoTrace.registerCategories();
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index bf34069f9445..23c43f56f2bc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -149,6 +149,8 @@ import android.annotation.UiContext;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ResourcesManager;
+import android.app.UiModeManager;
+import android.app.UiModeManager.ForceInvertStateChangeListener;
import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
import android.app.servertransaction.WindowStateTransactionItem;
@@ -164,7 +166,6 @@ import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.database.ContentObserver;
import android.graphics.BLASTBufferQueue;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -210,7 +211,6 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.Vibrator;
-import android.provider.Settings;
import android.sysprop.DisplayProperties;
import android.sysprop.ViewProperties;
import android.text.TextUtils;
@@ -463,10 +463,8 @@ public final class ViewRootImpl implements ViewParent,
private CompatOnBackInvokedCallback mCompatOnBackInvokedCallback;
@Nullable
- private ContentObserver mForceInvertObserver;
+ private ForceInvertStateChangeListener mForceInvertStateChangeListener;
- private static final int INVALID_VALUE = Integer.MIN_VALUE;
- private int mForceInvertEnabled = INVALID_VALUE;
/**
* Callback for notifying about global configuration changes.
*/
@@ -543,6 +541,8 @@ public final class ViewRootImpl implements ViewParent,
@UiContext
public final Context mContext;
+ private UiModeManager mUiModeManager;
+
@UnsupportedAppUsage
final IWindowSession mWindowSession;
@NonNull Display mDisplay;
@@ -1238,6 +1238,7 @@ public final class ViewRootImpl implements ViewParent,
public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session,
WindowLayout windowLayout) {
mContext = context;
+ mUiModeManager = context.getSystemService(UiModeManager.class);
mWindowSession = session;
mWindowLayout = windowLayout;
mDisplay = display;
@@ -1781,23 +1782,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private boolean isForceInvertEnabled() {
- if (mForceInvertEnabled == INVALID_VALUE) {
- reloadForceInvertEnabled();
- }
- return mForceInvertEnabled == 1;
- }
-
- private void reloadForceInvertEnabled() {
- if (forceInvertColor()) {
- mForceInvertEnabled = Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
- /* def= */ 0,
- UserHandle.myUserId());
- }
- }
-
/**
* Register any kind of listeners if setView was success.
*/
@@ -1829,21 +1813,11 @@ public final class ViewRootImpl implements ViewParent,
mBasePackageName);
if (forceInvertColor()) {
- if (mForceInvertObserver == null) {
- mForceInvertObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- reloadForceInvertEnabled();
- updateForceDarkMode();
- }
- };
- mContext.getContentResolver().registerContentObserver(
- Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED
- ),
- false,
- mForceInvertObserver,
- UserHandle.myUserId());
+ if (mForceInvertStateChangeListener == null) {
+ mForceInvertStateChangeListener =
+ forceInvertState -> updateForceDarkMode();
+ mUiModeManager.addForceInvertStateChangeListener(mExecutor,
+ mForceInvertStateChangeListener);
}
}
}
@@ -1861,9 +1835,10 @@ public final class ViewRootImpl implements ViewParent,
.unregisterDisplayListener(mDisplayListener);
if (forceInvertColor()) {
- if (mForceInvertObserver != null) {
- mContext.getContentResolver().unregisterContentObserver(mForceInvertObserver);
- mForceInvertObserver = null;
+ if (mForceInvertStateChangeListener != null) {
+ mUiModeManager.removeForceInvertStateChangeListener(
+ mForceInvertStateChangeListener);
+ mForceInvertStateChangeListener = null;
}
}
@@ -2050,21 +2025,25 @@ public final class ViewRootImpl implements ViewParent,
return getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
}
- /** Returns true if force dark should be enabled according to various settings */
+ /**
+ * Determines the type of force dark to apply, considering force inversion, system night mode,
+ * and app-specific settings (including developer opt-outs).
+ *
+ * @return A {@link ForceDarkType.ForceDarkTypeDef} constant indicating the force dark type.
+ */
@VisibleForTesting
public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() {
if (forceInvertColor()) {
// Force invert ignores all developer opt-outs.
// We also ignore dark theme, since the app developer can override the user's preference
- // for dark mode in configuration.uiMode. Instead, we assume that the force invert
- // setting will be enabled at the same time dark theme is in the Settings app.
- if (isForceInvertEnabled()) {
+ // for dark mode in configuration.uiMode. Instead, we assume that both force invert and
+ // the system's dark theme are enabled.
+ if (mUiModeManager.getForceInvertState() == UiModeManager.FORCE_INVERT_TYPE_DARK) {
return ForceDarkType.FORCE_INVERT_COLOR_DARK;
}
}
boolean useAutoDark = getNightMode() == Configuration.UI_MODE_NIGHT_YES;
-
if (useAutoDark) {
boolean forceDarkAllowedDefault =
SystemProperties.getBoolean(ThreadedRenderer.DEBUG_FORCE_DARK, false);
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index 4d354e0655f0..aa0111a13b8e 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -134,6 +134,7 @@ public interface ImeTracker {
ORIGIN_CLIENT,
ORIGIN_SERVER,
ORIGIN_IME,
+ ORIGIN_WM_SHELL,
})
@Retention(RetentionPolicy.SOURCE)
@interface Origin {}
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
index 7758dea3ea8a..e0c48b03dad8 100644
--- a/core/java/android/window/DesktopExperienceFlags.java
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -40,9 +40,7 @@ import java.util.function.BooleanSupplier;
* @hide
*/
public enum DesktopExperienceFlags {
- ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(
- com.android.server.display.feature.flags.Flags::enableDisplayContentModeManagement,
- true),
+ // go/keep-sorted start
ACTIVITY_EMBEDDING_SUPPORT_FOR_CONNECTED_DISPLAYS(
Flags::activityEmbeddingSupportForConnectedDisplays, false),
BASE_DENSITY_FOR_EXTERNAL_DISPLAYS(
@@ -53,6 +51,9 @@ public enum DesktopExperienceFlags {
ENABLE_CONNECTED_DISPLAYS_DND(Flags::enableConnectedDisplaysDnd, false),
ENABLE_CONNECTED_DISPLAYS_PIP(Flags::enableConnectedDisplaysPip, false),
ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG(Flags::enableConnectedDisplaysWindowDrag, false),
+ ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT(
+ com.android.server.display.feature.flags.Flags::enableDisplayContentModeManagement,
+ true),
ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS(Flags::enableDisplayFocusInShellTransitions, false),
ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING(Flags::enableDisplayWindowingModeSwitching, false),
ENABLE_DRAG_TO_MAXIMIZE(Flags::enableDragToMaximize, true),
@@ -63,9 +64,12 @@ public enum DesktopExperienceFlags {
false),
ENABLE_PER_DISPLAY_PACKAGE_CONTEXT_CACHE_IN_STATUSBAR_NOTIF(
Flags::enablePerDisplayPackageContextCacheInStatusbarNotif, false),
+ ENABLE_TASKBAR_CONNECTED_DISPLAYS(Flags::enableTaskbarConnectedDisplays, false),
ENTER_DESKTOP_BY_DEFAULT_ON_FREEFORM_DISPLAYS(Flags::enterDesktopByDefaultOnFreeformDisplays,
false),
- REPARENT_WINDOW_TOKEN_API(Flags::reparentWindowTokenApi, true);
+ REPARENT_WINDOW_TOKEN_API(Flags::reparentWindowTokenApi, true)
+ // go/keep-sorted end
+ ;
/**
* 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/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 9468301ac996..f86a4249c0e1 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -42,62 +42,68 @@ import java.util.function.BooleanSupplier;
*/
public enum DesktopModeFlags {
// All desktop mode related flags to be overridden by developer option toggle will be added here
- ENABLE_DESKTOP_WINDOWING_MODE(
- Flags::enableDesktopWindowingMode, /* shouldOverrideByDevOption= */ true),
- ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true),
- ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION(
- Flags::enableCaptionCompatInsetForceConsumption, true),
+ // go/keep-sorted start
+ DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
+ ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true),
+ ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION(Flags::enableCaptionCompatInsetForceConsumption,
+ true),
ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS(
Flags::enableCaptionCompatInsetForceConsumptionAlways, true),
ENABLE_CASCADING_WINDOWS(Flags::enableCascadingWindows, true),
- ENABLE_TILE_RESIZING(Flags::enableTileResizing, true),
- ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY(
- Flags::enableDesktopWindowingWallpaperActivity, true),
- ENABLE_DESKTOP_WINDOWING_MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true),
- ENABLE_THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true),
- ENABLE_HOLD_TO_DRAG_APP_HANDLE(Flags::enableHoldToDragAppHandle, true),
- ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true),
- ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true),
- ENABLE_TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
- ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
- DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
- ENABLE_WINDOWING_SCALED_RESIZING(Flags::enableWindowingScaledResizing, true),
- ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
+ ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX(
+ Flags::enableDesktopAppLaunchAlttabTransitionsBugfix, true),
+ ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX(Flags::enableDesktopAppLaunchTransitionsBugfix,
+ true),
+ ENABLE_DESKTOP_COMPAT_UI_VISIBILITY_STATUS(Flags::enableCompatUiVisibilityStatus, true),
+ ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX(
+ Flags::enableDesktopRecentsTransitionsCornersBugfix, false),
+ ENABLE_DESKTOP_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE_BUGFIX(
+ Flags::skipCompatUiEducationInDesktopMode, true),
+ ENABLE_DESKTOP_SYSTEM_DIALOGS_TRANSITIONS(Flags::enableDesktopSystemDialogsTransitions, true),
+ ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER(
+ Flags::enableDesktopWallpaperActivityForSystemUser, true),
+ ENABLE_DESKTOP_WINDOWING_APP_TO_WEB(Flags::enableDesktopWindowingAppToWeb, true),
+ ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION(Flags::enableDesktopWindowingAppToWebEducation,
+ true),
ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true),
- ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
- ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS(
- Flags::enableDesktopWindowingTaskbarRunningApps, true),
- ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
- Flags::enableWindowingTransitionHandlersObservers, false),
- ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false),
- ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true),
ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX(
Flags::enableDesktopWindowingEnterTransitionBugfix, true),
+ ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX(
+ Flags::enableDesktopWindowingExitByMinimizeTransitionBugfix, false),
ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX(
Flags::enableDesktopWindowingExitTransitionsBugfix, true),
- ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS_BUGFIX(
- Flags::enableDesktopAppLaunchAlttabTransitionsBugfix, true),
- ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX(
- Flags::enableDesktopAppLaunchTransitionsBugfix, true),
- ENABLE_DESKTOP_COMPAT_UI_VISIBILITY_STATUS(
- Flags::enableCompatUiVisibilityStatus, true),
- ENABLE_DESKTOP_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE_BUGFIX(
- Flags::skipCompatUiEducationInDesktopMode, true),
- INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
- Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true),
ENABLE_DESKTOP_WINDOWING_HSUM(Flags::enableDesktopWindowingHsum, true),
+ ENABLE_DESKTOP_WINDOWING_MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true),
+ ENABLE_DESKTOP_WINDOWING_MODE(Flags::enableDesktopWindowingMode, true),
+ ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES(
+ Flags::enableDesktopWindowingMultiInstanceFeatures, true),
+ ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false),
+ ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true),
+ ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
+ ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS(Flags::enableDesktopWindowingTaskbarRunningApps,
+ true),
+ ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
+ ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity,
+ true),
+ ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true),
+ ENABLE_HOLD_TO_DRAG_APP_HANDLE(Flags::enableHoldToDragAppHandle, true),
ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true),
ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS(Flags::enableTaskResizingKeyboardShortcuts, true),
- ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER(
- Flags::enableDesktopWallpaperActivityForSystemUser, true),
- ENABLE_TOP_VISIBLE_ROOT_TASK_PER_USER_TRACKING(
- Flags::enableTopVisibleRootTaskPerUserTracking, true),
- ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX(
- Flags::enableDesktopRecentsTransitionsCornersBugfix, false),
- ENABLE_DESKTOP_SYSTEM_DIALOGS_TRANSITIONS(Flags::enableDesktopSystemDialogsTransitions, true),
- ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES(
- Flags::enableDesktopWindowingMultiInstanceFeatures, true);
+ ENABLE_TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
+ ENABLE_THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true),
+ ENABLE_TILE_RESIZING(Flags::enableTileResizing, true),
+ ENABLE_TOP_VISIBLE_ROOT_TASK_PER_USER_TRACKING(Flags::enableTopVisibleRootTaskPerUserTracking,
+ true),
+ ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true),
+ ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
+ ENABLE_WINDOWING_SCALED_RESIZING(Flags::enableWindowingScaledResizing, true),
+ ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
+ Flags::enableWindowingTransitionHandlersObservers, false),
+ INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
+ Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true)
+ // go/keep-sorted end
+ ;
/**
* 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 d20b06738f8c..ed22ec73aac8 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -513,6 +513,13 @@ flag {
}
flag {
+ name: "enable_taskbar_connected_displays"
+ namespace: "lse_desktop_experience"
+ description: "Enables connected displays in taskbar."
+ bug: "393398093"
+}
+
+flag {
name: "enable_bug_fixes_for_secondary_display"
namespace: "lse_desktop_experience"
description: "Bugfixes / papercuts to bring Desktop Windowing to secondary displays."
@@ -610,3 +617,17 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_desktop_taskbar_on_freeform_displays"
+ namespace: "lse_desktop_experience"
+ description: "Forces pinned taskbar with desktop tasks on freeform displays"
+ bug: "390665752"
+}
+
+flag {
+ name: "enable_presentation_for_connected_displays"
+ namespace: "lse_desktop_experience"
+ description: "Enables full support of presentation API for connected displays."
+ bug: "378503083"
+}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 407790c89202..a673ad7dfb34 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -399,6 +399,7 @@ message ActivityRecordProto {
optional bool should_override_force_resize_app = 44;
optional bool should_enable_user_aspect_ratio_settings = 45;
optional bool is_user_fullscreen_override_enabled = 46;
+ optional int64 request_open_in_browser_education_timestamp = 47;
}
/* represents WindowToken */
diff --git a/core/res/res/drawable/accessibility_autoclick_button_rounded_background.xml b/core/res/res/drawable/accessibility_autoclick_button_rounded_background.xml
new file mode 100644
index 000000000000..6d0c2653e5f2
--- /dev/null
+++ b/core/res/res/drawable/accessibility_autoclick_button_rounded_background.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/materialColorSurfaceContainer" />
+ <corners android:radius="24dp" />
+</shape>
diff --git a/core/res/res/drawable/accessibility_autoclick_left_click.xml b/core/res/res/drawable/accessibility_autoclick_left_click.xml
new file mode 100644
index 000000000000..64c8efbe24b3
--- /dev/null
+++ b/core/res/res/drawable/accessibility_autoclick_left_click.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="18dp"
+ android:height="18dp"
+ android:viewportWidth="18"
+ android:viewportHeight="18">
+ <path
+ android:pathData="M5.7 12C4.1 11.9167 2.75 11.3 1.65 10.15C0.55 9 0 7.61667 0 6C0 4.33333 0.583333 2.91667 1.75 1.75C2.91667 0.583332 4.33333 -0.00000143051 6 -0.00000143051C7.61667 -0.00000143051 9 0.549999 10.15 1.65C11.3 2.75 11.9167 4.1 12 5.7L9.9 5.075C9.68333 4.175 9.21667 3.44167 8.5 2.875C7.78333 2.29167 6.95 2 6 2C4.9 2 3.95833 2.39167 3.175 3.175C2.39167 3.95833 2 4.9 2 6C2 6.95 2.28333 7.78333 2.85 8.5C3.43333 9.21667 4.175 9.68333 5.075 9.9L5.7 12ZM14.525 16.5L10.25 12.225L9 16L6 6L16 9L12.225 10.25L16.5 14.525L14.525 16.5Z"
+ android:fillColor="@color/materialColorPrimary" />
+</vector>
diff --git a/core/res/res/drawable/accessibility_autoclick_pause.xml b/core/res/res/drawable/accessibility_autoclick_pause.xml
new file mode 100644
index 000000000000..5251b2afed0d
--- /dev/null
+++ b/core/res/res/drawable/accessibility_autoclick_pause.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="@color/materialColorPrimary"
+ android:pathData="M6,19h4V5H6v14zm8,-14v14h4V5h-4z" />
+</vector>
diff --git a/core/res/res/drawable/accessibility_autoclick_position.xml b/core/res/res/drawable/accessibility_autoclick_position.xml
new file mode 100644
index 000000000000..8c98235e7d99
--- /dev/null
+++ b/core/res/res/drawable/accessibility_autoclick_position.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="21dp"
+ android:height="17dp"
+ android:viewportWidth="21"
+ android:viewportHeight="17">
+ <path
+ android:pathData="M0.400024 2.99961C0.400024 1.67413 1.47454 0.599609 2.80002 0.599609H17.2C18.5255 0.599609 19.6 1.67413 19.6 2.99961V14.9996C19.6 16.3251 18.5255 17.3996 17.2 17.3996H2.80002C1.47454 17.3996 0.400024 16.3251 0.400024 14.9996V2.99961ZM2.80002 2.99961H17.2V14.9996H2.80002V2.99961ZM10.6 10.1996C9.60591 10.1996 8.80002 11.0055 8.80002 11.9996C8.80002 12.9937 9.60591 13.7996 10.6 13.7996H14.2C15.1941 13.7996 16 12.9937 16 11.9996C16 11.0055 15.1941 10.1996 14.2 10.1996H10.6Z"
+ android:fillType="evenOdd"
+ android:fillColor="@color/materialColorPrimary" />
+</vector>
diff --git a/core/res/res/drawable/accessibility_autoclick_type_panel_rounded_background.xml b/core/res/res/drawable/accessibility_autoclick_type_panel_rounded_background.xml
new file mode 100644
index 000000000000..e367ba55ae7d
--- /dev/null
+++ b/core/res/res/drawable/accessibility_autoclick_type_panel_rounded_background.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/materialColorSurface" />
+ <corners android:radius="40dp" />
+</shape>
diff --git a/core/res/res/layout/accessibility_autoclick_type_panel.xml b/core/res/res/layout/accessibility_autoclick_type_panel.xml
new file mode 100644
index 000000000000..9aa47cc8d68b
--- /dev/null
+++ b/core/res/res/layout/accessibility_autoclick_type_panel.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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:id="@+id/accessibility_autoclick_type_panel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:background="@drawable/accessibility_autoclick_type_panel_rounded_background"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:id="@+id/accessibility_autoclick_left_click_layout"
+ style="@style/AccessibilityAutoclickPanelButtonLayoutStyle"
+ android:layout_marginEnd="@dimen/accessibility_autoclick_type_panel_button_spacing">
+
+ <ImageButton
+ android:id="@+id/accessibility_autoclick_left_click_button"
+ style="@style/AccessibilityAutoclickPanelImageButtonStyle"
+ android:contentDescription="@string/accessibility_autoclick_left_click"
+ android:src="@drawable/accessibility_autoclick_left_click" />
+ </LinearLayout>
+
+ <View
+ android:layout_width="@dimen/accessibility_autoclick_type_panel_divider_width"
+ android:layout_height="@dimen/accessibility_autoclick_type_panel_divider_height"
+ android:layout_marginEnd="@dimen/accessibility_autoclick_type_panel_button_spacing"
+ android:background="@color/materialColorSurfaceContainer" />
+
+ <LinearLayout
+ android:id="@+id/accessibility_autoclick_pause_layout"
+ style="@style/AccessibilityAutoclickPanelButtonLayoutStyle"
+ android:layout_marginEnd="@dimen/accessibility_autoclick_type_panel_button_spacing">
+
+ <ImageButton
+ android:id="@+id/accessibility_autoclick_pause_button"
+ style="@style/AccessibilityAutoclickPanelImageButtonStyle"
+ android:contentDescription="@string/accessibility_autoclick_pause"
+ android:src="@drawable/accessibility_autoclick_pause" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/accessibility_autoclick_position_layout"
+ style="@style/AccessibilityAutoclickPanelButtonLayoutStyle">
+
+ <ImageButton
+ android:id="@+id/accessibility_autoclick_position_button"
+ style="@style/AccessibilityAutoclickPanelImageButtonStyle"
+ android:contentDescription="@string/accessibility_autoclick_position"
+ android:src="@drawable/accessibility_autoclick_position" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
new file mode 100644
index 000000000000..11fc48668ad7
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_base.xml
@@ -0,0 +1,89 @@
+<?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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_header_height"
+ android:clipChildren="false"
+ android:tag="compactHUN"
+ android:gravity="center_vertical"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ android:importantForAccessibility="no">
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/notification_icon_circle_size"
+ android:layout_height="@dimen/notification_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:padding="@dimen/notification_icon_circle_padding"
+ android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_icon_circle_size"
+ />
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:orientation="horizontal"
+ >
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:gravity="center_vertical"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textSize="@dimen/notification_2025_title_text_size"
+ />
+ <include layout="@layout/notification_2025_top_line_views" />
+ </NotificationTopLineView>
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+ <include layout="@layout/notification_2025_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
+ </FrameLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
new file mode 100644
index 000000000000..dd70087f7785
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_compact_heads_up_messaging.xml
@@ -0,0 +1,116 @@
+<?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
+ -->
+
+<com.android.internal.widget.CompactMessagingLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_header_height"
+ android:clipChildren="false"
+ android:tag="compactMessagingHUN"
+ android:gravity="center_vertical"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ android:importantForAccessibility="no">
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/notification_icon_circle_size"
+ android:layout_height="@dimen/notification_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:padding="@dimen/notification_icon_circle_padding"
+ android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_icon_circle_size"
+ />
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/conversation_icon"
+ android:layout_width="@dimen/notification_icon_circle_size"
+ android:layout_height="@dimen/notification_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:clipToOutline="true"
+ android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_icon_circle_size"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no"
+ />
+ <ViewStub
+ android:layout="@layout/conversation_face_pile_layout"
+ android:layout_gravity="center_vertical|start"
+ android:layout_width="@dimen/conversation_compact_face_pile_size"
+ android:layout_height="@dimen/conversation_compact_face_pile_size"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:id="@+id/conversation_face_pile"
+ />
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:orientation="horizontal"
+ >
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:gravity="center_vertical"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textSize="@dimen/notification_2025_title_text_size"
+ />
+ <include layout="@layout/notification_2025_top_line_views" />
+ </NotificationTopLineView>
+ <FrameLayout
+ android:id="@+id/reply_action_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/notification_action_list_height"
+ android:gravity="center_vertical"
+ android:orientation="horizontal" />
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+ <include layout="@layout/notification_2025_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
+ </FrameLayout>
+ </LinearLayout>
+</com.android.internal.widget.CompactMessagingLayout>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 5644cf9dd61d..484e8ef1e049 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -714,6 +714,18 @@
<!-- The minimum window size of the accessibility window magnifier -->
<dimen name="accessibility_window_magnifier_min_size">122dp</dimen>
+ <!-- The accessibility autoclick panel button spacing -->
+ <dimen name="accessibility_autoclick_type_panel_button_spacing">12dp</dimen>
+
+ <!-- The accessibility autoclick panel button width and height -->
+ <dimen name="accessibility_autoclick_type_panel_button_size">36dp</dimen>
+
+ <!-- The accessibility autoclick panel divider width -->
+ <dimen name="accessibility_autoclick_type_panel_divider_width">1dp</dimen>
+
+ <!-- The accessibility autoclick panel divider height -->
+ <dimen name="accessibility_autoclick_type_panel_divider_height">24dp</dimen>
+
<!-- Margin around the various security views -->
<dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fa4c21de682e..9399e2b9824e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6149,6 +6149,16 @@
<!-- Label for Dpad center action [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_dpad_center_label">Dpad Center</string>
+ <!-- Accessibility autoclick related strings -->
+ <!-- Label for autoclick type settings panel [CHAR LIMIT=NONE] -->
+ <string name="accessibility_autoclick_type_settings_panel_title">Autoclick type settings panel</string>
+ <!-- Label for autoclick left click button [CHAR LIMIT=NONE] -->
+ <string name="accessibility_autoclick_left_click">Left click</string>
+ <!-- Label for autoclick pause button [CHAR LIMIT=NONE] -->
+ <string name="accessibility_autoclick_pause">Pause</string>
+ <!-- Label for autoclick position button [CHAR LIMIT=NONE] -->
+ <string name="accessibility_autoclick_position">Position</string>
+
<!-- Text to tell the user that a package has been forced by themselves in the RESTRICTED bucket. [CHAR LIMIT=NONE] -->
<string name="as_app_forced_to_restricted_bucket">
<xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 579dc91d2ca1..ee1edda838fd 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1745,6 +1745,22 @@ please see styles_device_defaults.xml.
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
+ <style name="AccessibilityAutoclickPanelButtonLayoutStyle">
+ <item name="android:gravity">center</item>
+ <item name="android:background">@drawable/accessibility_autoclick_button_rounded_background</item>
+ <item name="android:layout_width">@dimen/accessibility_autoclick_type_panel_button_size</item>
+ <item name="android:layout_height">@dimen/accessibility_autoclick_type_panel_button_size</item>
+ </style>
+
+ <style name="AccessibilityAutoclickPanelImageButtonStyle">
+ <item name="android:gravity">center</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:scaleType">center</item>
+ <item name="android:background">@android:color/transparent</item>
+ <item name="android:tint">@color/materialColorPrimary</item>
+ </style>
+
<!--
TODO(b/309578419): Make activities go edge-to-edge properly and then remove this.
-->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8bb3c995cf9f..653996afbc01 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2412,6 +2412,8 @@
<java-symbol type="layout" name="notification_2025_template_collapsed_base" />
<java-symbol type="layout" name="notification_2025_template_expanded_base" />
<java-symbol type="layout" name="notification_2025_template_heads_up_base" />
+ <java-symbol type="layout" name="notification_2025_template_compact_heads_up_base" />
+ <java-symbol type="layout" name="notification_2025_template_compact_heads_up_messaging" />
<java-symbol type="layout" name="notification_2025_template_header" />
<java-symbol type="layout" name="notification_2025_template_collapsed_messaging" />
<java-symbol type="layout" name="notification_2025_template_collapsed_media" />
@@ -5605,6 +5607,24 @@
<java-symbol type="bool" name="config_enable_a11y_fullscreen_magnification_overscroll_handler" />
<java-symbol type="dimen" name="accessibility_fullscreen_magnification_gesture_edge_slop" />
+ <!-- Accessibility autoclick related -->
+ <java-symbol type="layout" name="accessibility_autoclick_type_panel" />
+ <java-symbol type="string" name="accessibility_autoclick_type_settings_panel_title" />
+ <java-symbol type="string" name="accessibility_autoclick_left_click" />
+ <java-symbol type="string" name="accessibility_autoclick_pause" />
+ <java-symbol type="string" name="accessibility_autoclick_position" />
+ <java-symbol type="dimen" name="accessibility_autoclick_type_panel_button_spacing" />
+ <java-symbol type="dimen" name="accessibility_autoclick_type_panel_button_size" />
+ <java-symbol type="dimen" name="accessibility_autoclick_type_panel_divider_height" />
+ <java-symbol type="dimen" name="accessibility_autoclick_type_panel_divider_width" />
+ <java-symbol type="id" name="accessibility_autoclick_type_panel" />
+ <java-symbol type="id" name="accessibility_autoclick_left_click_layout" />
+ <java-symbol type="id" name="accessibility_autoclick_left_click_button" />
+ <java-symbol type="id" name="accessibility_autoclick_pause_layout" />
+ <java-symbol type="id" name="accessibility_autoclick_pause_button" />
+ <java-symbol type="id" name="accessibility_autoclick_position_layout" />
+ <java-symbol type="id" name="accessibility_autoclick_position_button" />
+
<!-- For HapticFeedbackConstants configurability defined at HapticFeedbackCustomization -->
<java-symbol type="string" name="config_hapticFeedbackCustomizationFile" />
<java-symbol type="xml" name="haptic_feedback_customization" />
diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
index 0b5a44665d2b..91efacf0dcc3 100644
--- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -59,6 +59,8 @@ import perfetto.protos.TrackEventOuterClass.TrackEvent;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* This class is used to test the native tracing support. Run this test
@@ -77,6 +79,8 @@ public class PerfettoTraceTest {
private static final String BAR = "bar";
private static final Category FOO_CATEGORY = new Category(FOO);
+ private static final int MESSAGE = 1234567;
+ private static final int MESSAGE_COUNT = 3;
private final Set<String> mCategoryNames = new ArraySet<>();
private final Set<String> mEventNames = new ArraySet<>();
@@ -592,6 +596,89 @@ public class PerfettoTraceTest {
assertThat(mDebugAnnotationNames).doesNotContain("before");
}
+ @Test
+ @RequiresFlagsEnabled(android.os.Flags.FLAG_PERFETTO_SDK_TRACING_V2)
+ public void testMessageQueue() throws Exception {
+ TraceConfig traceConfig = getTraceConfig("mq");
+
+ PerfettoTrace.MQ_CATEGORY.register();
+ final HandlerThread thread = new HandlerThread("test");
+ thread.start();
+ final Handler handler = thread.getThreadHandler();
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true,
+ getTraceConfig("mq").toByteArray());
+
+ handler.sendEmptyMessage(MESSAGE);
+ handler.sendEmptyMessageDelayed(MESSAGE, 10);
+ handler.sendEmptyMessage(MESSAGE);
+ handler.postDelayed(() -> {
+ latch.countDown();
+ }, 10);
+ assertThat(latch.await(100, TimeUnit.MILLISECONDS)).isTrue();
+
+ byte[] traceBytes = session.close();
+
+ Trace trace = Trace.parseFrom(traceBytes);
+
+ boolean hasTrackEvent = false;
+ int instantCount = 0;
+ int counterCount = 0;
+ int beginCount = 0;
+ int endCount = 0;
+
+ Set<Long> flowIds = new ArraySet<>();
+ for (TracePacket packet: trace.getPacketList()) {
+ TrackEvent event;
+ if (packet.hasTrackEvent()) {
+ hasTrackEvent = true;
+ event = packet.getTrackEvent();
+ } else {
+ continue;
+ }
+
+ List<DebugAnnotation> annotations = event.getDebugAnnotationsList();
+ switch (event.getType()) {
+ case TrackEvent.Type.TYPE_INSTANT:
+ if (annotations.get(2).getIntValue() == MESSAGE) {
+ instantCount++;
+ assertThat(annotations.get(0).getStringValue()).isEqualTo("test");
+ assertThat(event.getFlowIdsCount()).isEqualTo(1);
+ flowIds.addAll(event.getFlowIdsList());
+ }
+ break;
+ case TrackEvent.Type.TYPE_COUNTER:
+ counterCount++;
+ break;
+ case TrackEvent.Type.TYPE_SLICE_BEGIN:
+ annotations = event.getDebugAnnotationsList();
+ if (flowIds.containsAll(event.getTerminatingFlowIdsList())) {
+ beginCount++;
+ assertThat(event.getTerminatingFlowIdsCount()).isEqualTo(1);
+ }
+ break;
+ case TrackEvent.Type.TYPE_SLICE_END:
+ endCount++;
+ break;
+ default:
+ break;
+ }
+ collectInternedData(packet);
+ }
+
+ assertThat(hasTrackEvent).isTrue();
+ assertThat(mCategoryNames).contains("mq");
+ assertThat(mEventNames).contains("message_queue_send");
+ assertThat(mEventNames).contains("message_queue_receive");
+ assertThat(mDebugAnnotationNames).contains("what");
+ assertThat(mDebugAnnotationNames).contains("delay");
+ assertThat(instantCount).isEqualTo(MESSAGE_COUNT);
+ assertThat(beginCount).isEqualTo(MESSAGE_COUNT);
+ assertThat(endCount).isAtLeast(MESSAGE_COUNT);
+ assertThat(counterCount).isAtLeast(MESSAGE_COUNT);
+ }
+
private TrackEvent getTrackEvent(Trace trace, int idx) {
int curIdx = 0;
for (TracePacket packet: trace.getPacketList()) {
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index c40137f1bd34..a289df0441e5 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.app.UiModeManager.MODE_NIGHT_NO;
+import static android.app.UiModeManager.MODE_NIGHT_YES;
import static android.util.SequenceUtils.getInitSeq;
import static android.view.HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING;
import static android.view.InputDevice.SOURCE_ROTARY_ENCODER;
@@ -67,8 +69,10 @@ import static org.junit.Assume.assumeTrue;
import android.annotation.NonNull;
import android.app.Instrumentation;
import android.app.UiModeManager;
+import android.app.UiModeManager.ForceInvertType;
import android.content.Context;
import android.graphics.ForceDarkType;
+import android.graphics.ForceDarkType.ForceDarkTypeDef;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
@@ -93,9 +97,12 @@ import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.TestUtils;
import com.android.cts.input.BlockingQueueEventVerifier;
import com.android.window.flags.Flags;
+import com.google.common.truth.Expect;
+
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.AfterClass;
@@ -124,6 +131,8 @@ public class ViewRootImplTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final Expect mExpect = Expect.create();
private ViewRootImpl mViewRootImpl;
private View mView;
@@ -1507,49 +1516,34 @@ public class ViewRootImplTest {
}
@Test
- public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
- ShellIdentityUtils.invokeWithShellPermissions(() -> {
- Settings.Secure.putInt(
- sContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
- /* value= */ 0
- );
- var uiModeManager = sContext.getSystemService(UiModeManager.class);
- uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
- });
-
- sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
-
- assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
- }
-
- @Test
- public void forceInvertOnDarkThemeOff_forceDarkModeEnabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
- ShellIdentityUtils.invokeWithShellPermissions(() -> {
- Settings.Secure.putInt(
- sContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
- /* value= */ 1
- );
- var uiModeManager = sContext.getSystemService(UiModeManager.class);
- uiModeManager.setNightMode(UiModeManager.MODE_NIGHT_NO);
- });
-
- sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
-
- assertThat(mViewRootImpl.determineForceDarkType())
- .isEqualTo(ForceDarkType.FORCE_INVERT_COLOR_DARK);
+ @RequiresFlagsEnabled(FLAG_FORCE_INVERT_COLOR)
+ public void updateConfiguration_returnsExpectedForceDarkMode() {
+ waitForSystemNightModeActivated(true);
+
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK);
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_DARK, ForceDarkType.FORCE_INVERT_COLOR_DARK);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+
+ waitForSystemNightModeActivated(false);
+
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ true, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ true,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
+ verifyForceDarkType(/* isAppInNightMode= */ false, /* isForceInvertEnabled= */ false,
+ UiModeManager.FORCE_INVERT_TYPE_OFF, ForceDarkType.NONE);
}
@Test
+ @EnableFlags(FLAG_FORCE_INVERT_COLOR)
public void forceInvertOffForceDarkOff_forceDarkModeDisabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
ShellIdentityUtils.invokeWithShellPermissions(() -> {
Settings.Secure.putInt(
sContext.getContentResolver(),
@@ -1562,15 +1556,14 @@ public class ViewRootImplTest {
});
sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
+ mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
}
@Test
+ @EnableFlags(FLAG_FORCE_INVERT_COLOR)
public void forceInvertOffForceDarkOn_forceDarkModeEnabled() {
- mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
ShellIdentityUtils.invokeWithShellPermissions(() -> {
Settings.Secure.putInt(
sContext.getContentResolver(),
@@ -1582,8 +1575,7 @@ public class ViewRootImplTest {
});
sInstrumentation.runOnMainSync(() ->
- mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
- );
+ mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.FORCE_DARK);
}
@@ -1790,4 +1782,39 @@ public class ViewRootImplTest {
() -> view.getViewTreeObserver().removeOnDrawListener(listener));
}
}
+
+ private void waitForSystemNightModeActivated(boolean active) {
+ ShellIdentityUtils.invokeWithShellPermissions(() ->
+ sInstrumentation.runOnMainSync(() -> {
+ var uiModeManager = sContext.getSystemService(UiModeManager.class);
+ uiModeManager.setNightModeActivated(active);
+ }));
+ sInstrumentation.waitForIdleSync();
+ }
+
+ private void verifyForceDarkType(boolean isAppInNightMode, boolean isForceInvertEnabled,
+ @ForceInvertType int expectedForceInvertType,
+ @ForceDarkTypeDef int expectedForceDarkType) {
+ var uiModeManager = sContext.getSystemService(UiModeManager.class);
+ ShellIdentityUtils.invokeWithShellPermissions(() -> {
+ uiModeManager.setApplicationNightMode(
+ isAppInNightMode ? MODE_NIGHT_YES : MODE_NIGHT_NO);
+ Settings.Secure.putInt(
+ sContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+ isForceInvertEnabled ? 1 : 0);
+ });
+
+ sInstrumentation.runOnMainSync(() ->
+ mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId()));
+ try {
+ TestUtils.waitUntil("Waiting for force invert state changed",
+ () -> (uiModeManager.getForceInvertState() == expectedForceInvertType));
+ } catch (Exception e) {
+ Log.e(TAG, "Unexpected error trying to apply force invert state. " + e);
+ e.printStackTrace();
+ }
+
+ mExpect.that(mViewRootImpl.determineForceDarkType()).isEqualTo(expectedForceDarkType);
+ }
}
diff --git a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
index 36c73e2e979e..c42ddd3412be 100644
--- a/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
+++ b/core/tests/overlaytests/host/src/com/android/server/om/hosttest/InstallOverlayTests.java
@@ -44,8 +44,11 @@ public class InstallOverlayTests extends BaseHostJUnit4Test {
"com.android.server.om.hosttest.update_overlay_test";
private static final String DEVICE_TEST_CLS = DEVICE_TEST_PKG + ".UpdateOverlayTest";
+ private int mCurrentUserid;
+
@Before
public void ensureNoOverlays() throws Exception {
+ mCurrentUserid = getDevice().getCurrentUser();
// Make sure we're starting with a clean slate.
for (String pkg : ALL_PACKAGES) {
assertFalse(pkg + " should not be installed", isPackageInstalled(pkg));
@@ -62,7 +65,7 @@ public class InstallOverlayTests extends BaseHostJUnit4Test {
@After
public void uninstallOverlays() throws Exception {
for (String pkg : ALL_PACKAGES) {
- uninstallPackage(pkg);
+ getDevice().uninstallPackageForUser(pkg, mCurrentUserid);
}
}
@@ -166,7 +169,7 @@ public class InstallOverlayTests extends BaseHostJUnit4Test {
installPackage("OverlayHostTests_AppOverlayV1.apk");
assertTrue(getDevice().executeShellCommand("cat /data/system/overlays.xml")
.contains(APP_OVERLAY_PACKAGE_NAME));
- uninstallPackage(APP_OVERLAY_PACKAGE_NAME);
+ getDevice().uninstallPackageForUser(APP_OVERLAY_PACKAGE_NAME, mCurrentUserid);
delay();
assertFalse(getDevice().executeShellCommand("cat /data/system/overlays.xml")
.contains(APP_OVERLAY_PACKAGE_NAME));
@@ -200,12 +203,12 @@ public class InstallOverlayTests extends BaseHostJUnit4Test {
}
private void installPackage(String pkg) throws Exception {
- super.installPackage(pkg);
+ super.installPackageAsUser(pkg, true /* grantPermission */, mCurrentUserid);
delay();
}
private void installInstantPackage(String pkg) throws Exception {
- super.installPackage(pkg, "--instant");
+ super.installPackageAsUser(pkg, true /* grantPermission */, mCurrentUserid, "--instant");
delay();
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
index 77acbde7a465..bce6c5999a75 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleControllerBubbleBarTest.kt
@@ -47,6 +47,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.shared.TransactionPool
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.BubbleBarUpdate
+import com.android.wm.shell.shared.bubbles.DeviceConfig
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
index 9711889ad028..ec1add21ebf8 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -31,6 +31,7 @@ import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.DeviceConfig
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
index d3cfbd00c4a3..68b3d8822525 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
@@ -42,10 +42,10 @@ import com.android.wm.shell.bubbles.BubbleLogger
import com.android.wm.shell.bubbles.BubbleOverflow
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.BubbleTaskView
-import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
import com.android.wm.shell.bubbles.FakeBubbleFactory
import com.android.wm.shell.common.TestShellExecutor
+import com.android.wm.shell.shared.bubbles.DeviceConfig
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewController
import com.android.wm.shell.taskview.TaskViewTaskController
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
index 7f65e22736b3..037bd227d33c 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
@@ -39,11 +39,11 @@ import com.android.wm.shell.bubbles.BubbleLogger
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.bubbles.BubbleTaskView
import com.android.wm.shell.bubbles.BubbleTaskViewFactory
-import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.bubbles.FakeBubbleExpandedViewManager
import com.android.wm.shell.bubbles.RegionSamplingProvider
import com.android.wm.shell.bubbles.UiEventSubject.Companion.assertThat
import com.android.wm.shell.common.TestShellExecutor
+import com.android.wm.shell.shared.bubbles.DeviceConfig
import com.android.wm.shell.shared.handles.RegionSamplingHelper
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewController
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index d4cbe6e10971..1b0e11f7103c 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -30,13 +30,13 @@ import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.bubbles.DeviceConfig
import com.android.wm.shell.shared.bubbles.BaseBubblePinController
import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.DeviceConfig
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/shared/Android.bp b/libs/WindowManager/Shell/shared/Android.bp
index 261c63948a94..af46ca298efe 100644
--- a/libs/WindowManager/Shell/shared/Android.bp
+++ b/libs/WindowManager/Shell/shared/Android.bp
@@ -74,6 +74,7 @@ java_library {
"**/desktopmode/*.kt",
],
static_libs: [
+ "WindowManager-Shell-shared-AOSP",
"com.android.window.flags.window-aconfig-java",
"wm_shell-shared-utils",
],
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DeviceConfig.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DeviceConfig.kt
index 929330918174..f479da051e06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DeviceConfig.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DeviceConfig.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.bubbles
+package com.android.wm.shell.shared.bubbles
import android.content.Context
import android.content.res.Configuration
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index e196880aad0f..da62be7f142f 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -18,6 +18,8 @@ package com.android.wm.shell.shared.desktopmode;
import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
+import static com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper.enableBubbleToFullscreen;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -230,7 +232,7 @@ public class DesktopModeStatus {
* Return {@code true} if desktop mode dev option should be shown on current device
*/
public static boolean canShowDesktopExperienceDevOption(@NonNull Context context) {
- return Flags.showDesktopExperienceDevOption();
+ return Flags.showDesktopExperienceDevOption() && isDeviceEligibleForDesktopMode(context);
}
/** Returns if desktop mode dev option should be enabled if there is no user override. */
@@ -270,7 +272,8 @@ public class DesktopModeStatus {
* necessarily enabling desktop mode
*/
public static boolean overridesShowAppHandle(@NonNull Context context) {
- return Flags.showAppHandleLargeScreens() && deviceHasLargeScreen(context);
+ return (Flags.showAppHandleLargeScreens() || enableBubbleToFullscreen())
+ && deviceHasLargeScreen(context);
}
/**
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 1a03eb5e4a42..5aed9e910dd3 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
@@ -115,6 +115,7 @@ import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
+import com.android.wm.shell.shared.bubbles.DeviceConfig;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
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 0e2fc3a77c6d..8cf3f7afd46a 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
@@ -33,6 +33,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.R;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.DeviceConfig;
/**
* Keeps track of display size, configuration, and specific bubble sizes. One place for all
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 2b2112fd461f..3babc0d801c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -91,6 +91,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.bubbles.DeviceConfig;
import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
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 f3f8d6f96a42..b1035bc177a1 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
@@ -45,11 +45,11 @@ import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
-import com.android.wm.shell.bubbles.DeviceConfig;
import com.android.wm.shell.bubbles.DismissViewUtils;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedViewDragController.DragListener;
import com.android.wm.shell.shared.bubbles.BaseBubblePinController;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.DeviceConfig;
import com.android.wm.shell.shared.bubbles.DismissView;
import kotlin.Unit;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
new file mode 100644
index 000000000000..c10c2c905c97
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
@@ -0,0 +1,159 @@
+/*
+ * 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.wm.shell.common.pip;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import android.app.ActivityManager;
+import android.window.DisplayAreaInfo;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import com.android.window.flags.Flags;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+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.pip2.phone.PipTransition;
+
+import java.util.Optional;
+
+/** Helper class for PiP on Desktop Mode. */
+public class PipDesktopState {
+ private final PipDisplayLayoutState mPipDisplayLayoutState;
+ private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional;
+ private final Optional<DesktopWallpaperActivityTokenProvider>
+ mDesktopWallpaperActivityTokenProviderOptional;
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+
+ public PipDesktopState(PipDisplayLayoutState pipDisplayLayoutState,
+ Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
+ Optional<DesktopWallpaperActivityTokenProvider>
+ desktopWallpaperActivityTokenProviderOptional,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ mPipDisplayLayoutState = pipDisplayLayoutState;
+ mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional;
+ mDesktopWallpaperActivityTokenProviderOptional =
+ desktopWallpaperActivityTokenProviderOptional;
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+ }
+
+ /**
+ * Returns whether PiP in Desktop Windowing is enabled by checking the following:
+ * - Desktop Windowing in PiP flag is enabled
+ * - DesktopWallpaperActivityTokenProvider is injected
+ * - DesktopUserRepositories is injected
+ */
+ public boolean isDesktopWindowingPipEnabled() {
+ return Flags.enableDesktopWindowingPip()
+ && mDesktopWallpaperActivityTokenProviderOptional.isPresent()
+ && mDesktopUserRepositoriesOptional.isPresent();
+ }
+
+ /** Returns whether PiP in Connected Displays is enabled by checking the flag. */
+ public boolean isConnectedDisplaysPipEnabled() {
+ return Flags.enableConnectedDisplaysPip();
+ }
+
+ /** Returns whether the display with the PiP task is in freeform windowing mode. */
+ private boolean isDisplayInFreeform() {
+ final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(
+ mPipDisplayLayoutState.getDisplayId());
+ if (tdaInfo != null) {
+ return tdaInfo.configuration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FREEFORM;
+ }
+ return false;
+ }
+
+ /** Returns whether PiP is exiting while we're in a Desktop Mode session. */
+ private boolean isPipExitingToDesktopMode() {
+ // Early return if PiP in Desktop Windowing is not supported.
+ if (!isDesktopWindowingPipEnabled()) {
+ return false;
+ }
+ final int displayId = mPipDisplayLayoutState.getDisplayId();
+ return getDesktopRepository().getVisibleTaskCount(displayId) > 0
+ || getDesktopWallpaperActivityTokenProvider().isWallpaperActivityVisible(displayId)
+ || isDisplayInFreeform();
+ }
+
+ /** Returns whether {@param pipTask} would be entering in a Desktop Mode session. */
+ public boolean isPipEnteringInDesktopMode(ActivityManager.RunningTaskInfo pipTask) {
+ // Early return if PiP in Desktop Windowing is not supported.
+ if (!isDesktopWindowingPipEnabled()) {
+ return false;
+ }
+ final DesktopRepository desktopRepository = getDesktopRepository();
+ return desktopRepository.getVisibleTaskCount(pipTask.getDisplayId()) > 0
+ || desktopRepository.isMinimizedPipPresentInDisplay(pipTask.getDisplayId());
+ }
+
+ /**
+ * Invoked when an EXIT_PiP transition is detected in {@link PipTransition}.
+ * Returns whether the PiP exiting should also trigger the active Desktop Mode session to exit.
+ */
+ public boolean shouldExitPipExitDesktopMode() {
+ // Early return if PiP in Desktop Windowing is not supported.
+ if (!isDesktopWindowingPipEnabled()) {
+ return false;
+ }
+ final int displayId = mPipDisplayLayoutState.getDisplayId();
+ return getDesktopRepository().getVisibleTaskCount(displayId) == 0
+ && getDesktopWallpaperActivityTokenProvider().isWallpaperActivityVisible(displayId);
+ }
+
+ /**
+ * Returns a {@link WindowContainerTransaction} that reorders the {@link WindowContainerToken}
+ * of the DesktopWallpaperActivity for the display with the given {@param displayId}.
+ */
+ public WindowContainerTransaction getWallpaperActivityTokenWct(int displayId) {
+ return new WindowContainerTransaction().reorder(
+ getDesktopWallpaperActivityTokenProvider().getToken(displayId), /* onTop= */ false);
+ }
+
+ /**
+ * The windowing mode to restore to when resizing out of PIP direction.
+ * Defaults to undefined and can be overridden to restore to an alternate windowing mode.
+ */
+ public int getOutPipWindowingMode() {
+ // If we are exiting PiP while the device is in Desktop mode (the task should expand to
+ // freeform windowing mode):
+ // 1) If the display windowing mode is freeform, set windowing mode to UNDEFINED so it will
+ // resolve the windowing mode to the display's windowing mode.
+ // 2) If the display windowing mode is not FREEFORM, set windowing mode to FREEFORM.
+ if (isPipExitingToDesktopMode()) {
+ if (isDisplayInFreeform()) {
+ return WINDOWING_MODE_UNDEFINED;
+ } else {
+ return WINDOWING_MODE_FREEFORM;
+ }
+ }
+
+ // By default, or if the task is going to fullscreen, reset the windowing mode to undefined.
+ return WINDOWING_MODE_UNDEFINED;
+ }
+
+ private DesktopRepository getDesktopRepository() {
+ return mDesktopUserRepositoriesOptional.get().getCurrent();
+ }
+
+ private DesktopWallpaperActivityTokenProvider getDesktopWallpaperActivityTokenProvider() {
+ return mDesktopWallpaperActivityTokenProviderOptional.get();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS
index 752d2fd721a5..8ab53eaa5eea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS
@@ -1,2 +1,3 @@
# WM Shell sub-module dagger owners
-jorgegil@google.com \ No newline at end of file
+jorgegil@google.com
+madym@google.com \ No newline at end of file
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 413300612f7d..e7c76bbd91b2 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
@@ -31,6 +31,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDesktopState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipPerfHintController;
@@ -84,14 +85,11 @@ public abstract class Pip2Module {
@NonNull PipDisplayLayoutState pipDisplayLayoutState,
@NonNull PipUiStateChangeController pipUiStateChangeController,
DisplayController displayController,
- Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
- Optional<DesktopWallpaperActivityTokenProvider>
- desktopWallpaperActivityTokenProviderOptional) {
+ PipDesktopState pipDesktopState) {
return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
pipBoundsState, null, pipBoundsAlgorithm, pipTaskListener,
pipScheduler, pipStackListenerController, pipDisplayLayoutState,
- pipUiStateChangeController, displayController, desktopUserRepositoriesOptional,
- desktopWallpaperActivityTokenProviderOptional);
+ pipUiStateChangeController, displayController, pipDesktopState);
}
@WMSingleton
@@ -142,13 +140,9 @@ public abstract class Pip2Module {
PipBoundsState pipBoundsState,
@ShellMainThread ShellExecutor mainExecutor,
PipTransitionState pipTransitionState,
- Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
- Optional<DesktopWallpaperActivityTokenProvider>
- desktopWallpaperActivityTokenProviderOptional,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ PipDesktopState pipDesktopState) {
return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState,
- desktopUserRepositoriesOptional, desktopWallpaperActivityTokenProviderOptional,
- rootTaskDisplayAreaOrganizer);
+ pipDesktopState);
}
@WMSingleton
@@ -233,4 +227,17 @@ public abstract class Pip2Module {
return new PipTaskListener(context, shellTaskOrganizer, pipTransitionState,
pipScheduler, pipBoundsState, pipBoundsAlgorithm, mainExecutor);
}
+
+ @WMSingleton
+ @Provides
+ static PipDesktopState providePipDesktopState(
+ PipDisplayLayoutState pipDisplayLayoutState,
+ Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
+ Optional<DesktopWallpaperActivityTokenProvider>
+ desktopWallpaperActivityTokenProviderOptional,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
+ ) {
+ return new PipDesktopState(pipDisplayLayoutState, desktopUserRepositoriesOptional,
+ desktopWallpaperActivityTokenProviderOptional, rootTaskDisplayAreaOrganizer);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 32ee319a053b..621ccba40db2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -59,6 +59,8 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
/**
* Animated visual indicator for Desktop Mode windowing transitions.
@@ -149,12 +151,19 @@ public class DesktopModeVisualIndicator {
// left, and split right for the right edge. This is universal across all drag event types.
if (inputCoordinates.x < 0) return TO_SPLIT_LEFT_INDICATOR;
if (inputCoordinates.x > layout.width()) return TO_SPLIT_RIGHT_INDICATOR;
- // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
- // In drags not originating on a freeform caption, we should default to a TO_DESKTOP
- // indicator.
- IndicatorType result = mDragStartState == DragStartState.FROM_FREEFORM
- ? NO_INDICATOR
- : TO_DESKTOP_INDICATOR;
+ IndicatorType result;
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()
+ && !DesktopModeStatus.canEnterDesktopMode(mContext)) {
+ // If desktop is not available, default to "no indicator"
+ result = NO_INDICATOR;
+ } else {
+ // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
+ // In drags not originating on a freeform caption, we should default to a TO_DESKTOP
+ // indicator.
+ result = mDragStartState == DragStartState.FROM_FREEFORM
+ ? NO_INDICATOR
+ : TO_DESKTOP_INDICATOR;
+ }
final int transitionAreaWidth = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.desktop_mode_transition_region_thickness);
// Because drags in freeform use task position for indicator calculation, we need to
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 3f88e7bddd34..475515053bfe 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
@@ -1528,16 +1528,11 @@ class DesktopTasksController(
private fun addWallpaperActivity(displayId: Int, wct: WindowContainerTransaction) {
logV("addWallpaperActivity")
if (ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER.isTrue()) {
-
- // If the wallpaper activity for this display already exists, let's reorder it to top.
- val wallpaperActivityToken = desktopWallpaperActivityTokenProvider.getToken(displayId)
- if (wallpaperActivityToken != null) {
- wct.reorder(wallpaperActivityToken, /* onTop= */ true)
- return
- }
-
val intent = Intent(context, DesktopWallpaperActivity::class.java)
- if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ if (
+ desktopWallpaperActivityTokenProvider.getToken(displayId) == null &&
+ Flags.enablePerDisplayDesktopWallpaperActivity()
+ ) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 21b0820f523a..e17587ff18bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -16,14 +16,10 @@
package com.android.wm.shell.pip2.phone;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.SurfaceControl;
-import android.window.DisplayAreaInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -32,20 +28,14 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
-import com.android.window.flags.Flags;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
-import com.android.wm.shell.desktopmode.DesktopUserRepositories;
-import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
+import com.android.wm.shell.common.pip.PipDesktopState;
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.protolog.ShellProtoLogGroup;
-import java.util.Objects;
-import java.util.Optional;
-
/**
* Scheduler for Shell initiated PiP transitions and animations.
*/
@@ -56,10 +46,7 @@ public class PipScheduler {
private final PipBoundsState mPipBoundsState;
private final ShellExecutor mMainExecutor;
private final PipTransitionState mPipTransitionState;
- private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional;
- private final Optional<DesktopWallpaperActivityTokenProvider>
- mDesktopWallpaperActivityTokenProviderOptional;
- private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+ private final PipDesktopState mPipDesktopState;
private PipTransitionController mPipTransitionController;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
@@ -72,18 +59,12 @@ public class PipScheduler {
PipBoundsState pipBoundsState,
ShellExecutor mainExecutor,
PipTransitionState pipTransitionState,
- Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
- Optional<DesktopWallpaperActivityTokenProvider>
- desktopWallpaperActivityTokenProviderOptional,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ PipDesktopState pipDesktopState) {
mContext = context;
mPipBoundsState = pipBoundsState;
mMainExecutor = mainExecutor;
mPipTransitionState = pipTransitionState;
- mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional;
- mDesktopWallpaperActivityTokenProviderOptional =
- desktopWallpaperActivityTokenProviderOptional;
- mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+ mPipDesktopState = pipDesktopState;
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
@@ -105,7 +86,7 @@ public class PipScheduler {
wct.setBounds(pipTaskToken, null);
// if we are hitting a multi-activity case
// windowing mode change will reparent to original host task
- wct.setWindowingMode(pipTaskToken, getOutPipWindowingMode());
+ wct.setWindowingMode(pipTaskToken, mPipDesktopState.getOutPipWindowingMode());
return wct;
}
@@ -235,55 +216,6 @@ public class PipScheduler {
maybeUpdateMovementBounds();
}
- /** Returns whether the display is in freeform windowing mode. */
- private boolean isDisplayInFreeform() {
- final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(
- Objects.requireNonNull(mPipTransitionState.getPipTaskInfo()).displayId);
- if (tdaInfo != null) {
- return tdaInfo.configuration.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_FREEFORM;
- }
- return false;
- }
-
- /** Returns whether PiP is exiting while we're in desktop mode. */
- private boolean isPipExitingToDesktopMode() {
- // Early return if PiP in Desktop Windowing is not supported.
- if (!Flags.enableDesktopWindowingPip() || mDesktopUserRepositoriesOptional.isEmpty()
- || mDesktopWallpaperActivityTokenProviderOptional.isEmpty()) {
- return false;
- }
- final int displayId = Objects.requireNonNull(
- mPipTransitionState.getPipTaskInfo()).displayId;
- return mDesktopUserRepositoriesOptional.get().getCurrent().getVisibleTaskCount(displayId)
- > 0
- || mDesktopWallpaperActivityTokenProviderOptional.get().isWallpaperActivityVisible(
- displayId)
- || isDisplayInFreeform();
- }
-
- /**
- * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined
- * and can be overridden to restore to an alternate windowing mode.
- */
- private int getOutPipWindowingMode() {
- // If we are exiting PiP while the device is in Desktop mode (the task should expand to
- // freeform windowing mode):
- // 1) If the display windowing mode is freeform, set windowing mode to undefined so it will
- // resolve the windowing mode to the display's windowing mode.
- // 2) If the display windowing mode is not freeform, set windowing mode to freeform.
- if (isPipExitingToDesktopMode()) {
- if (isDisplayInFreeform()) {
- return WINDOWING_MODE_UNDEFINED;
- } else {
- return WINDOWING_MODE_FREEFORM;
- }
- }
-
- // By default, or if the task is going to fullscreen, reset the windowing mode to undefined.
- return WINDOWING_MODE_UNDEFINED;
- }
-
@VisibleForTesting
void setSurfaceControlTransactionFactory(
@NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index d735375b0fc9..dbcbf3663827 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -132,8 +132,9 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
"onTaskInfoChanged: %s, state=%s oldParams=%s newParams=%s",
taskInfo.topActivity, mPipTransitionState, mPictureInPictureParams, params);
setPictureInPictureParams(params);
+ // Note: params is nullable while mPictureInPictureParams is never null
float newAspectRatio = mPictureInPictureParams.getAspectRatioFloat();
- if (params.hasSetAspectRatio()
+ if (mPictureInPictureParams.hasSetAspectRatio()
&& mPipBoundsAlgorithm.isValidPictureInPictureAspectRatio(newAspectRatio)
&& PipUtils.aspectRatioChanged(newAspectRatio, mPipBoundsState.getAspectRatio())) {
mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
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 78aa686a3a0e..bb9b479524e5 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
@@ -56,19 +56,16 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
import com.android.internal.util.Preconditions;
-import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ComponentUtils;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDesktopState;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMenuController;
import com.android.wm.shell.common.pip.PipUtils;
-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;
@@ -79,8 +76,6 @@ import com.android.wm.shell.shared.pip.PipContentOverlay;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
-import java.util.Optional;
-
/**
* Implementation of transitions for PiP on phone.
*/
@@ -117,9 +112,7 @@ public class PipTransition extends PipTransitionController implements
private final PipDisplayLayoutState mPipDisplayLayoutState;
private final DisplayController mDisplayController;
private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
- private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional;
- private final Optional<DesktopWallpaperActivityTokenProvider>
- mDesktopWallpaperActivityTokenProviderOptional;
+ private final PipDesktopState mPipDesktopState;
//
// Transition caches
@@ -159,9 +152,7 @@ public class PipTransition extends PipTransitionController implements
PipDisplayLayoutState pipDisplayLayoutState,
PipUiStateChangeController pipUiStateChangeController,
DisplayController displayController,
- Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
- Optional<DesktopWallpaperActivityTokenProvider>
- desktopWallpaperActivityTokenProviderOptional) {
+ PipDesktopState pipDesktopState) {
super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
pipBoundsAlgorithm);
@@ -174,9 +165,7 @@ public class PipTransition extends PipTransitionController implements
mPipDisplayLayoutState = pipDisplayLayoutState;
mDisplayController = displayController;
mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(mContext);
- mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional;
- mDesktopWallpaperActivityTokenProviderOptional =
- desktopWallpaperActivityTokenProviderOptional;
+ mPipDesktopState = pipDesktopState;
}
@Override
@@ -802,7 +791,7 @@ public class PipTransition extends PipTransitionController implements
&& getFixedRotationDelta(info, pipTaskChange) == ROTATION_90) {
adjustedSourceRectHint.offset(cutoutInsets.left, cutoutInsets.top);
}
- if (Flags.enableDesktopWindowingPip()) {
+ if (mPipDesktopState.isDesktopWindowingPipEnabled()) {
adjustedSourceRectHint.offset(-pipActivityChange.getStartAbsBounds().left,
-pipActivityChange.getStartAbsBounds().top);
}
@@ -862,7 +851,7 @@ public class PipTransition extends PipTransitionController implements
// If PiP is enabled on Connected Displays, update PipDisplayLayoutState to have the correct
// display info that PiP is entering in.
- if (Flags.enableConnectedDisplaysPip()) {
+ if (mPipDesktopState.isConnectedDisplaysPipEnabled()) {
final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(
pipTask.displayId);
if (displayLayout != null) {
@@ -910,12 +899,7 @@ public class PipTransition extends PipTransitionController implements
// Since opening a new task while in Desktop Mode always first open in Fullscreen
// until DesktopMode Shell code resolves it to Freeform, PipTransition will get a
// possibility to handle it also. In this case return false to not have it enter PiP.
- final boolean isInDesktopSession = !mDesktopUserRepositoriesOptional.isEmpty()
- && (mDesktopUserRepositoriesOptional.get().getCurrent().getVisibleTaskCount(
- pipTask.displayId) > 0
- || mDesktopUserRepositoriesOptional.get().getCurrent()
- .isMinimizedPipPresentInDisplay(pipTask.displayId));
- if (isInDesktopSession) {
+ if (mPipDesktopState.isPipEnteringInDesktopMode(pipTask)) {
return false;
}
@@ -1089,26 +1073,13 @@ public class PipTransition extends PipTransitionController implements
"Unexpected bundle for " + mPipTransitionState);
break;
case PipTransitionState.EXITED_PIP:
- final TaskInfo pipTask = mPipTransitionState.getPipTaskInfo();
- final boolean desktopPipEnabled = Flags.enableDesktopWindowingPip()
- && mDesktopUserRepositoriesOptional.isPresent()
- && mDesktopWallpaperActivityTokenProviderOptional.isPresent();
- if (desktopPipEnabled && pipTask != null) {
- final DesktopRepository desktopRepository =
- mDesktopUserRepositoriesOptional.get().getCurrent();
- final boolean wallpaperIsVisible =
- mDesktopWallpaperActivityTokenProviderOptional.get()
- .isWallpaperActivityVisible(pipTask.displayId);
- if (desktopRepository.getVisibleTaskCount(pipTask.displayId) == 0
- && wallpaperIsVisible) {
- mTransitions.startTransition(
- TRANSIT_TO_BACK,
- new WindowContainerTransaction().reorder(
- mDesktopWallpaperActivityTokenProviderOptional.get()
- .getToken(pipTask.displayId), /* onTop= */ false),
- null
- );
- }
+ if (mPipDesktopState.shouldExitPipExitDesktopMode()) {
+ mTransitions.startTransition(
+ TRANSIT_TO_BACK,
+ mPipDesktopState.getWallpaperActivityTokenWct(
+ mPipTransitionState.getPipTaskInfo().getDisplayId()),
+ null /* firstHandler */
+ );
}
mPipTransitionState.setPinnedTaskLeash(null);
mPipTransitionState.setPipTaskInfo(null);
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 6a2a7b6becdc..fb4ce13c441f 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
@@ -131,6 +131,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.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
@@ -1439,13 +1440,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mDragToDesktopAnimationStartBounds.set(
relevantDecor.mTaskInfo.configuration.windowConfiguration.getBounds());
boolean dragFromStatusBarAllowed = false;
+ final int windowingMode = relevantDecor.mTaskInfo.getWindowingMode();
if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
// In proto2 any full screen or multi-window task can be dragged to
// freeform.
- final int windowingMode = relevantDecor.mTaskInfo.getWindowingMode();
dragFromStatusBarAllowed = windowingMode == WINDOWING_MODE_FULLSCREEN
|| windowingMode == WINDOWING_MODE_MULTI_WINDOW;
}
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ // TODO(b/388851898): add support for split screen (multi-window wm mode)
+ dragFromStatusBarAllowed = windowingMode == WINDOWING_MODE_FULLSCREEN;
+ }
final boolean shouldStartTransitionDrag =
relevantDecor.checkTouchEventInFocusedCaptionHandle(ev)
|| DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue();
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 c1a6240516c1..afb234899339 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
@@ -483,7 +483,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) {
Trace.beginSection("DesktopModeWindowDecoration#relayout");
- if (Flags.enableDesktopWindowingAppToWeb()) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_APP_TO_WEB.isTrue()) {
setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index 32a2f8294877..1d9564948772 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -252,6 +252,7 @@ class HandleMenu(
view = handleMenuView.rootView,
forciblyShownTypes = if (forceShowSystemBars) { systemBars() } else { 0 },
ignoreCutouts = Flags.showAppHandleLargeScreens()
+ || BubbleAnythingFlagHelper.enableBubbleToFullscreen()
)
} else {
parentDecor.addWindow(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 1264c013faf5..2948fdaf16af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -41,6 +41,7 @@ import com.android.internal.policy.SystemBarUtils
import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.shared.animation.Interpolators
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
@@ -146,6 +147,7 @@ internal class AppHandleViewHolder(
taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
ignoreCutouts = Flags.showAppHandleLargeScreens()
+ || BubbleAnythingFlagHelper.enableBubbleToFullscreen()
)
val view = statusBarInputLayer?.view ?: error("Unable to find statusBarInputLayer View")
val lp = statusBarInputLayer?.lp ?: error("Unable to find statusBarInputLayer " +
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index ab1ac1a0efa3..f767861addf9 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -19,7 +19,7 @@ package com.android.wm.shell.flicker
import android.tools.PlatformConsts.DESKTOP_MODE_MINIMUM_WINDOW_HEIGHT
import android.tools.PlatformConsts.DESKTOP_MODE_MINIMUM_WINDOW_WIDTH
import android.tools.flicker.AssertionInvocationGroup
-import android.tools.flicker.assertors.assertions.AppLayerIncreasesInSize
+import android.tools.flicker.assertors.assertions.ResizeVeilKeepsIncreasingInSize
import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart
@@ -36,6 +36,7 @@ import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayHeight
import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayWidth
import android.tools.flicker.assertors.assertions.AppWindowHasSizeOfAtLeast
import android.tools.flicker.assertors.assertions.AppWindowInsideDisplayBoundsAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowIsBiggerThanInitialBoundsAtEnd
import android.tools.flicker.assertors.assertions.AppWindowIsInvisibleAtEnd
import android.tools.flicker.assertors.assertions.AppWindowIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppWindowMaintainsAspectRatioAlways
@@ -168,9 +169,12 @@ class DesktopModeFlickerScenarios {
TaggedCujTransitionMatcher(associatedTransitionRequired = false)
)
.build(),
- // TODO(373638597) Add AppLayerIncreasesInSize assertion
- assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS
- )
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(
+ ResizeVeilKeepsIncreasingInSize(DESKTOP_MODE_APP),
+ AppWindowIsBiggerThanInitialBoundsAtEnd(DESKTOP_MODE_APP),
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING })
+ )
val EDGE_RESIZE =
FlickerConfigEntry(
@@ -184,7 +188,8 @@ class DesktopModeFlickerScenarios {
.build(),
assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
listOf(
- AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ ResizeVeilKeepsIncreasingInSize(DESKTOP_MODE_APP),
+ AppWindowIsBiggerThanInitialBoundsAtEnd(DESKTOP_MODE_APP),
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
@@ -223,9 +228,9 @@ class DesktopModeFlickerScenarios {
assertions =
AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
listOf(
- // TODO(373638597) Add AppLayerIncreasesInSize assertion
AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP),
- AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP)
+ AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP),
+ ResizeVeilKeepsIncreasingInSize(DESKTOP_MODE_APP),
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
@@ -368,7 +373,7 @@ class DesktopModeFlickerScenarios {
),
assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
listOf(
- AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ ResizeVeilKeepsIncreasingInSize(DESKTOP_MODE_APP),
AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP),
AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP)
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
@@ -393,7 +398,7 @@ class DesktopModeFlickerScenarios {
assertions =
AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
listOf(
- AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ ResizeVeilKeepsIncreasingInSize(DESKTOP_MODE_APP),
AppWindowMaintainsAspectRatioAlways(DESKTOP_MODE_APP),
AppWindowHasMaxBoundsInOnlyOneDimension(DESKTOP_MODE_APP)
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppWithExternalDisplayConnectedTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppWithExternalDisplayConnectedTest.kt
new file mode 100644
index 000000000000..cc9a799fb50c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppWithExternalDisplayConnectedTest.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.OpenAppWithExternalDisplayConnected
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [OpenAppWithExternalDisplayConnected]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class OpenAppWithExternalDisplayConnectedTest : OpenAppWithExternalDisplayConnected()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppWithExternalDisplayConnected.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppWithExternalDisplayConnected.kt
new file mode 100644
index 000000000000..81c46f13b384
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppWithExternalDisplayConnected.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.hardware.display.VirtualDisplay
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.traces.parsers.WindowManagerStateHelper
+import android.util.DisplayMetrics
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import com.android.wm.shell.ExtendedDisplaySettingsSession
+import com.android.wm.shell.Utils
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+/**
+ * Base scenario test for launching an app in desktop mode by default when an external display is
+ * connected.
+ */
+@Ignore("Test Base Class")
+abstract class OpenAppWithExternalDisplayConnected
+constructor(private val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+ private val displayManager =
+ instrumentation.getContext().getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ private var virtualDisplay: VirtualDisplay? = null
+
+ private val extendedDisplaySettingsSession =
+ ExtendedDisplaySettingsSession(instrumentation.context.contentResolver)
+
+ @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+ Assume.assumeTrue(Flags.enableDisplayWindowingModeSwitching())
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ extendedDisplaySettingsSession.open()
+ virtualDisplay = displayManager.createVirtualDisplay(
+ /* displayName= */ DISPLAY_NAME,
+ /* width= */ DISPLAY_WIDTH,
+ /* height= */ DISPLAY_HEIGHT,
+ /* densityDpi= */ DisplayMetrics.DENSITY_DEFAULT,
+ /* surface= */ null,
+ /* flags= */ DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC or
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY or
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED
+ )
+ }
+
+ @Test
+ open fun openAppWithExternalDisplayConnected() {
+ testApp.open()
+ }
+
+ @After
+ fun teardown() {
+ testApp.exit(wmHelper)
+ virtualDisplay?.let {
+ it.release()
+ }
+ extendedDisplaySettingsSession.close()
+ }
+
+ companion object {
+ const val DISPLAY_NAME = "testVirtualDisplay"
+ const val DISPLAY_HEIGHT = 600
+ const val DISPLAY_WIDTH = 800
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/ExtendedDisplaySettingsSession.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/ExtendedDisplaySettingsSession.kt
new file mode 100644
index 000000000000..0b2aacd00aa6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/ExtendedDisplaySettingsSession.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.wm.shell
+
+import android.content.ContentResolver
+import android.provider.Settings
+import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+
+class ExtendedDisplaySettingsSession(private val contentResolver: ContentResolver) {
+ private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+ private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
+
+ fun open() {
+ Settings.Global.putInt(contentResolver, settingName, 1)
+ }
+
+ fun close() {
+ Settings.Global.putInt(contentResolver, settingName, initialValue)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java
new file mode 100644
index 000000000000..75ad621e1cad
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.wm.shell.common.pip;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static com.android.window.flags.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_PIP;
+import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.platform.test.annotations.EnableFlags;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.window.DisplayAreaInfo;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+/**
+ * Unit test against {@link PipDesktopState}.
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+public class PipDesktopStateTest {
+ @Mock private PipDisplayLayoutState mMockPipDisplayLayoutState;
+ @Mock private Optional<DesktopUserRepositories> mMockDesktopUserRepositoriesOptional;
+ @Mock private Optional<DesktopWallpaperActivityTokenProvider>
+ mMockDesktopWallpaperActivityTokenProviderOptional;
+ @Mock private DesktopUserRepositories mMockDesktopUserRepositories;
+ @Mock private DesktopWallpaperActivityTokenProvider mMockDesktopWallpaperActivityTokenProvider;
+ @Mock private DesktopRepository mMockDesktopRepository;
+ @Mock private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
+ @Mock private ActivityManager.RunningTaskInfo mMockTaskInfo;
+
+ private static final int DISPLAY_ID = 1;
+ private DisplayAreaInfo mDefaultTda;
+ private PipDesktopState mPipDesktopState;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockDesktopUserRepositoriesOptional.get()).thenReturn(mMockDesktopUserRepositories);
+ when(mMockDesktopWallpaperActivityTokenProviderOptional.get()).thenReturn(
+ mMockDesktopWallpaperActivityTokenProvider);
+ when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mMockDesktopRepository);
+ when(mMockDesktopUserRepositoriesOptional.isPresent()).thenReturn(true);
+ when(mMockDesktopWallpaperActivityTokenProviderOptional.isPresent()).thenReturn(true);
+
+ when(mMockTaskInfo.getDisplayId()).thenReturn(DISPLAY_ID);
+ when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(DISPLAY_ID);
+
+ mDefaultTda = new DisplayAreaInfo(Mockito.mock(WindowContainerToken.class), DISPLAY_ID, 0);
+ when(mMockRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DISPLAY_ID)).thenReturn(
+ mDefaultTda);
+
+ mPipDesktopState = new PipDesktopState(mMockPipDisplayLayoutState,
+ mMockDesktopUserRepositoriesOptional,
+ mMockDesktopWallpaperActivityTokenProviderOptional,
+ mMockRootTaskDisplayAreaOrganizer);
+ }
+
+ @Test
+ public void isDesktopWindowingPipEnabled_returnsTrue() {
+ assertTrue(mPipDesktopState.isDesktopWindowingPipEnabled());
+ }
+
+ @Test
+ public void isDesktopWindowingPipEnabled_desktopRepositoryEmpty_returnsFalse() {
+ when(mMockDesktopUserRepositoriesOptional.isPresent()).thenReturn(false);
+
+ assertFalse(mPipDesktopState.isDesktopWindowingPipEnabled());
+ }
+
+ @Test
+ public void isDesktopWindowingPipEnabled_desktopWallpaperEmpty_returnsFalse() {
+ when(mMockDesktopWallpaperActivityTokenProviderOptional.isPresent()).thenReturn(false);
+
+ assertFalse(mPipDesktopState.isDesktopWindowingPipEnabled());
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CONNECTED_DISPLAYS_PIP)
+ public void isConnectedDisplaysPipEnabled_returnsTrue() {
+ assertTrue(mPipDesktopState.isConnectedDisplaysPipEnabled());
+ }
+
+ @Test
+ public void isPipEnteringInDesktopMode_visibleCountZero_minimizedPipPresent_returnsTrue() {
+ when(mMockDesktopRepository.getVisibleTaskCount(DISPLAY_ID)).thenReturn(0);
+ when(mMockDesktopRepository.isMinimizedPipPresentInDisplay(DISPLAY_ID)).thenReturn(true);
+
+ assertTrue(mPipDesktopState.isPipEnteringInDesktopMode(mMockTaskInfo));
+ }
+
+ @Test
+ public void isPipEnteringInDesktopMode_visibleCountNonzero_minimizedPipAbsent_returnsTrue() {
+ when(mMockDesktopRepository.getVisibleTaskCount(DISPLAY_ID)).thenReturn(1);
+ when(mMockDesktopRepository.isMinimizedPipPresentInDisplay(DISPLAY_ID)).thenReturn(false);
+
+ assertTrue(mPipDesktopState.isPipEnteringInDesktopMode(mMockTaskInfo));
+ }
+
+ @Test
+ public void isPipEnteringInDesktopMode_visibleCountZero_minimizedPipAbsent_returnsFalse() {
+ when(mMockDesktopRepository.getVisibleTaskCount(DISPLAY_ID)).thenReturn(0);
+ when(mMockDesktopRepository.isMinimizedPipPresentInDisplay(DISPLAY_ID)).thenReturn(false);
+
+ assertFalse(mPipDesktopState.isPipEnteringInDesktopMode(mMockTaskInfo));
+ }
+
+ @Test
+ public void shouldExitPipExitDesktopMode_visibleCountZero_wallpaperInvisible_returnsFalse() {
+ when(mMockDesktopRepository.getVisibleTaskCount(DISPLAY_ID)).thenReturn(0);
+ when(mMockDesktopWallpaperActivityTokenProvider.isWallpaperActivityVisible(
+ DISPLAY_ID)).thenReturn(false);
+
+ assertFalse(mPipDesktopState.shouldExitPipExitDesktopMode());
+ }
+
+ @Test
+ public void shouldExitPipExitDesktopMode_visibleCountNonzero_wallpaperVisible_returnsFalse() {
+ when(mMockDesktopRepository.getVisibleTaskCount(DISPLAY_ID)).thenReturn(1);
+ when(mMockDesktopWallpaperActivityTokenProvider.isWallpaperActivityVisible(
+ DISPLAY_ID)).thenReturn(true);
+
+ assertFalse(mPipDesktopState.shouldExitPipExitDesktopMode());
+ }
+
+ @Test
+ public void shouldExitPipExitDesktopMode_visibleCountZero_wallpaperVisible_returnsTrue() {
+ when(mMockDesktopRepository.getVisibleTaskCount(DISPLAY_ID)).thenReturn(0);
+ when(mMockDesktopWallpaperActivityTokenProvider.isWallpaperActivityVisible(
+ DISPLAY_ID)).thenReturn(true);
+
+ assertTrue(mPipDesktopState.shouldExitPipExitDesktopMode());
+ }
+
+ @Test
+ public void getOutPipWindowingMode_exitToDesktop_displayFreeform_returnsUndefined() {
+ // Set visible task count to 1 so isPipExitingToDesktopMode returns true
+ when(mMockDesktopRepository.getVisibleTaskCount(DISPLAY_ID)).thenReturn(1);
+ setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+
+ assertEquals(WINDOWING_MODE_UNDEFINED, mPipDesktopState.getOutPipWindowingMode());
+ }
+
+ @Test
+ public void getOutPipWindowingMode_exitToDesktop_displayFullscreen_returnsFreeform() {
+ // Set visible task count to 1 so isPipExitingToDesktopMode returns true
+ when(mMockDesktopRepository.getVisibleTaskCount(DISPLAY_ID)).thenReturn(1);
+ setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ assertEquals(WINDOWING_MODE_FREEFORM, mPipDesktopState.getOutPipWindowingMode());
+ }
+
+ @Test
+ public void getOutPipWindowingMode_exitToFullscreen_displayFullscreen_returnsUndefined() {
+ setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ assertEquals(WINDOWING_MODE_UNDEFINED, mPipDesktopState.getOutPipWindowingMode());
+ }
+
+ private void setDisplayWindowingMode(int windowingMode) {
+ mDefaultTda.configuration.windowConfiguration.setWindowingMode(windowingMode);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index e46d2c7147ed..13b44977e9c7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -19,22 +19,30 @@ package com.android.wm.shell.desktopmode
import android.app.ActivityManager.RunningTaskInfo
import android.graphics.PointF
import android.graphics.Rect
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
import androidx.test.filters.SmallTest
import com.android.internal.policy.SystemBarUtils
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
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.SyncTransactionQueue
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.google.common.truth.Truth.assertThat
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
/**
@@ -43,9 +51,19 @@ import org.mockito.kotlin.whenever
* Usage: atest WMShellUnitTests:DesktopModeVisualIndicatorTest
*/
@SmallTest
+@RunWithLooper
@RunWith(AndroidTestingRunner::class)
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
class DesktopModeVisualIndicatorTest : ShellTestCase() {
- @Mock private lateinit var taskInfo: RunningTaskInfo
+
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ @JvmField
+ @Rule
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!!
+
+ private lateinit var taskInfo: RunningTaskInfo
@Mock private lateinit var syncQueue: SyncTransactionQueue
@Mock private lateinit var displayController: DisplayController
@Mock private lateinit var taskSurface: SurfaceControl
@@ -56,10 +74,13 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
@Before
fun setUp() {
+ whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
whenever(displayLayout.stableInsets()).thenReturn(STABLE_INSETS)
whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
+ whenever(displayController.getDisplay(anyInt())).thenReturn(mContext.display)
+ taskInfo = DesktopTestHelpers.createFullscreenTask()
}
@Test
@@ -190,6 +211,39 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
}
+ @Test
+ @EnableFlags(
+ com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
+ com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
+ )
+ fun testDefaultIndicatorWithNoDesktop() {
+ whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(false)
+
+ createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
+ var result = visualIndicator.updateIndicatorType(PointF(500f, 500f))
+ assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+
+ result = visualIndicator.updateIndicatorType(PointF(10000f, 500f))
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR)
+
+ result = visualIndicator.updateIndicatorType(PointF(-10000f, 500f))
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR)
+
+ createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
+ result = visualIndicator.updateIndicatorType(PointF(500f, 500f))
+ assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+
+ result = visualIndicator.updateIndicatorType(PointF(500f, 0f))
+ assertThat(result)
+ .isEqualTo(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR)
+
+ createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
+ result = visualIndicator.updateIndicatorType(PointF(500f, 500f))
+ assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
+ }
+
private fun createVisualIndicator(dragStartState: DesktopModeVisualIndicator.DragStartState) {
visualIndicator =
DesktopModeVisualIndicator(
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 8e7545c2a99c..0428ba58240d 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
@@ -541,7 +541,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
markTaskHidden(task1)
@@ -730,7 +729,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
markTaskVisible(task1)
@@ -769,8 +767,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_someAppsInvisible_desktopWallpaperEnabled_reordersOnlyFreeformTasks() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
+ fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() {
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
markTaskHidden(task1)
@@ -787,24 +784,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
wct.assertReorderAt(index = 2, task2)
}
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_someAppsInvisible_desktopWallpaperEnabled_reordersAll() {
- val task1 = setUpFreeformTask()
- val task2 = setUpFreeformTask()
- markTaskHidden(task1)
- markTaskVisible(task2)
-
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct =
- getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- assertThat(wct.hierarchyOps).hasSize(3)
- // Expect order to be from bottom: wallpaper intent, task1, task2
- wct.assertReorderAt(index = 0, wallpaperToken)
- wct.assertReorderAt(index = 1, task1)
- wct.assertReorderAt(index = 2, task2)
- }
-
@Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() {
@@ -820,9 +799,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_noActiveTasks_desktopWallpaperEnabled_addsDesktopWallpaper() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
-
+ fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() {
controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
val wct =
@@ -831,16 +808,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_noActiveTasks_desktopWallpaperEnabled_reordersDesktopWallpaper() {
- controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
-
- val wct =
- getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
- wct.assertReorderAt(index = 0, wallpaperToken)
- }
-
- @Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() {
taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -864,7 +831,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
@@ -909,7 +875,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_desktopWallpaperEnabled_dontReorderMinimizedTask() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val homeTask = setUpHomeTask()
val freeformTask = setUpFreeformTask()
val minimizedTask = setUpFreeformTask()
@@ -1375,7 +1340,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val task = createTaskInfo(1)
whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
@@ -1558,7 +1522,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val freeformTask = setUpFreeformTask()
val fullscreenTask = setUpFullscreenTask()
markTaskHidden(freeformTask)
@@ -1661,7 +1624,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun moveRunningTaskToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
val newTask = setUpFullscreenTask()
val homeTask = setUpHomeTask()
@@ -2682,7 +2644,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
@@ -2814,7 +2775,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val freeformTask1 = setUpFreeformTask()
val freeformTask2 = createFreeformTask()
@@ -2849,9 +2809,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val task = createFreeformTask()
-
val result = controller.handleRequest(Binder(), createTransition(task))
assertNotNull(result, "Should handle request")
@@ -2879,7 +2837,6 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
- whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
// Second display task
createFreeformTask(displayId = SECOND_DISPLAY)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
index bd857c7dcd45..8e0381e4f933 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
@@ -26,7 +26,6 @@ import static org.mockito.kotlin.MatchersKt.eq;
import static org.mockito.kotlin.VerificationKt.times;
import static org.mockito.kotlin.VerificationKt.verify;
-import android.app.TaskInfo;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Matrix;
@@ -39,12 +38,9 @@ import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
-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.common.pip.PipDesktopState;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
@@ -55,11 +51,8 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
-
/**
* Unit test against {@link PipScheduler}
*/
@@ -77,16 +70,13 @@ public class PipSchedulerTest {
@Mock private PipBoundsState mMockPipBoundsState;
@Mock private ShellExecutor mMockMainExecutor;
@Mock private PipTransitionState mMockPipTransitionState;
+ @Mock private PipDesktopState mMockPipDesktopState;
@Mock private PipTransitionController mMockPipTransitionController;
@Mock private Runnable mMockUpdateMovementBoundsRunnable;
@Mock private WindowContainerToken mMockPipTaskToken;
@Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
@Mock private SurfaceControl.Transaction mMockTransaction;
@Mock private PipAlphaAnimator mMockAlphaAnimator;
- @Mock private DesktopUserRepositories mMockDesktopUserRepositories;
- @Mock private DesktopWallpaperActivityTokenProvider mMockDesktopWallpaperActivityTokenProvider;
- @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
-
@Captor private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
@Captor private ArgumentCaptor<WindowContainerTransaction> mWctArgumentCaptor;
@@ -101,14 +91,9 @@ public class PipSchedulerTest {
when(mMockFactory.getTransaction()).thenReturn(mMockTransaction);
when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any()))
.thenReturn(mMockTransaction);
- when(mMockDesktopUserRepositories.getCurrent())
- .thenReturn(Mockito.mock(DesktopRepository.class));
- when(mMockPipTransitionState.getPipTaskInfo()).thenReturn(Mockito.mock(TaskInfo.class));
mPipScheduler = new PipScheduler(mMockContext, mMockPipBoundsState, mMockMainExecutor,
- mMockPipTransitionState, Optional.of(mMockDesktopUserRepositories),
- Optional.of(mMockDesktopWallpaperActivityTokenProvider),
- mRootTaskDisplayAreaOrganizer);
+ mMockPipTransitionState, mMockPipDesktopState);
mPipScheduler.setPipTransitionController(mMockPipTransitionController);
mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory);
mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, startTx, finishTx, direction) ->
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
index 3923a1ef5633..1b462c30e017 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -165,6 +165,25 @@ public class PipTaskListenerTest {
}
@Test
+ public void onTaskInfoChanged_withNullPipParams_doNothing() {
+ mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
+ mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
+ mMockPipBoundsAlgorithm, mMockShellExecutor);
+ mPipTaskListener.addParamsChangedListener(mMockPipParamsChangedCallback);
+ Rational aspectRatio = new Rational(4, 3);
+ when(mMockPipBoundsState.getAspectRatio()).thenReturn(aspectRatio.toFloat());
+ String action1 = "action1";
+ mPipTaskListener.onTaskInfoChanged(getTaskInfo(aspectRatio, action1));
+
+ clearInvocations(mMockPipParamsChangedCallback);
+ mPipTaskListener.onTaskInfoChanged(new ActivityManager.RunningTaskInfo());
+
+ verifyZeroInteractions(mMockPipParamsChangedCallback);
+ verify(mMockPipTransitionState, times(0))
+ .setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+ }
+
+ @Test
public void onTaskInfoChanged_withActionsChanged_callbackActionsChanged() {
mPipTaskListener = new PipTaskListener(mMockContext, mMockShellTaskOrganizer,
mMockPipTransitionState, mMockPipScheduler, mMockPipBoundsState,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
index 4dac99b14aaf..8ebe414111b5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
@@ -56,6 +56,7 @@ class DesktopModeStatusTest : ShellTestCase() {
doReturn(false).whenever(mockResources).getBoolean(
eq(R.bool.config_isDesktopModeDevOptionSupported)
)
+ setDeviceEligibleForDesktopMode(false)
doReturn(context.contentResolver).whenever(mockContext).contentResolver
resetDesktopModeFlagsCache()
resetEnforceDeviceRestriction()
@@ -74,7 +75,7 @@ class DesktopModeStatusTest : ShellTestCase() {
Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
)
@Test
- fun canEnterDesktopMode_DWFlagDisabled_configsOff_returnsFalse() {
+ fun canEnterDesktopMode_DWFlagDisabled_deviceNotEligible_returnsFalse() {
assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
}
@@ -83,8 +84,8 @@ class DesktopModeStatusTest : ShellTestCase() {
Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
)
@Test
- fun canEnterDesktopMode_DWFlagDisabled_configsOn_disableDeviceRestrictions_returnsFalse() {
- doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+ fun canEnterDesktopMode_DWFlagDisabled_deviceEligible_configDevOptionOn_returnsFalse() {
+ setDeviceEligibleForDesktopMode(true)
doReturn(true).whenever(mockResources).getBoolean(
eq(R.bool.config_isDesktopModeDevOptionSupported)
)
@@ -98,7 +99,7 @@ class DesktopModeStatusTest : ShellTestCase() {
Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
)
@Test
- fun canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_returnsFalse() {
+ fun canEnterDesktopMode_DWFlagDisabled_deviceNotEligible_configDevOptionOn_returnsFalse() {
doReturn(true).whenever(mockResources).getBoolean(
eq(R.bool.config_isDesktopModeDevOptionSupported)
)
@@ -111,7 +112,7 @@ class DesktopModeStatusTest : ShellTestCase() {
Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
)
@Test
- fun canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_flagOverrideOn_returnsTrue() {
+ fun canEnterDesktopMode_DWFlagDisabled_deviceNotEligible_forceUsingDevOption_returnsTrue() {
doReturn(true).whenever(mockResources).getBoolean(
eq(R.bool.config_isDesktopModeDevOptionSupported)
)
@@ -123,14 +124,7 @@ class DesktopModeStatusTest : ShellTestCase() {
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
@Test
- fun canEnterDesktopMode_DWFlagEnabled_configsOff_returnsFalse() {
- assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
- }
-
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- @Test
- fun canEnterDesktopMode_DWFlagEnabled_configDesktopModeOff_returnsFalse() {
+ fun canEnterDesktopMode_DWFlagEnabled_deviceNotEligible_returnsFalse() {
doReturn(true).whenever(mockResources).getBoolean(
eq(R.bool.config_isDesktopModeDevOptionSupported)
)
@@ -141,8 +135,8 @@ class DesktopModeStatusTest : ShellTestCase() {
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
@Test
- fun canEnterDesktopMode_DWFlagEnabled_configDesktopModeOn_returnsTrue() {
- doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+ fun canEnterDesktopMode_DWFlagEnabled_deviceEligible_returnsTrue() {
+ setDeviceEligibleForDesktopMode(true)
assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
}
@@ -150,16 +144,7 @@ class DesktopModeStatusTest : ShellTestCase() {
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
@Test
- fun canEnterDesktopMode_DWFlagEnabled_configsOff_disableDeviceRestrictions_returnsTrue() {
- disableEnforceDeviceRestriction()
-
- assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
- }
-
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
- @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
- @Test
- fun canEnterDesktopMode_DWFlagEnabled_configDevOptionOn_flagOverrideOn_returnsTrue() {
+ fun canEnterDesktopMode_DWFlagEnabled_deviceNotEligible_forceUsingDevOption_returnsTrue() {
doReturn(true).whenever(mockResources).getBoolean(
eq(R.bool.config_isDesktopModeDevOptionSupported)
)
@@ -198,6 +183,28 @@ class DesktopModeStatusTest : ShellTestCase() {
assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isTrue()
}
+ @DisableFlags(Flags.FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION)
+ @Test
+ fun canShowDesktopExperienceDevOption_flagDisabled_returnsFalse() {
+ setDeviceEligibleForDesktopMode(true)
+
+ assertThat(DesktopModeStatus.canShowDesktopExperienceDevOption(mockContext)).isFalse()
+ }
+
+ @EnableFlags(Flags.FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION)
+ @Test
+ fun canShowDesktopExperienceDevOption_flagEnabled_deviceNotEligible_returnsFalse() {
+ assertThat(DesktopModeStatus.canShowDesktopExperienceDevOption(mockContext)).isFalse()
+ }
+
+ @EnableFlags(Flags.FLAG_SHOW_DESKTOP_EXPERIENCE_DEV_OPTION)
+ @Test
+ fun canShowDesktopExperienceDevOption_flagEnabled_deviceEligible_returnsTrue() {
+ setDeviceEligibleForDesktopMode(true)
+
+ assertThat(DesktopModeStatus.canShowDesktopExperienceDevOption(mockContext)).isTrue()
+ }
+
private fun resetEnforceDeviceRestriction() {
setEnforceDeviceRestriction(true)
}
@@ -232,4 +239,10 @@ class DesktopModeStatusTest : ShellTestCase() {
DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, override.setting
)
}
+
+ private fun setDeviceEligibleForDesktopMode(eligible: Boolean) {
+ val deviceRestrictions = DesktopModeStatus::class.java.getDeclaredField("ENFORCE_DEVICE_RESTRICTIONS")
+ deviceRestrictions.isAccessible = true
+ deviceRestrictions.setBoolean(/* obj= */ null, /* z= */ !eligible)
+ }
}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 12b1dd794a03..3dc53c5051e9 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -288,6 +288,7 @@ cc_benchmark {
"tests/AttributeResolution_bench.cpp",
"tests/CursorWindow_bench.cpp",
"tests/Generic_bench.cpp",
+ "tests/LocaleDataLookup_bench.cpp",
"tests/SparseEntry_bench.cpp",
"tests/Theme_bench.cpp",
],
diff --git a/libs/androidfw/LocaleDataLookup.cpp b/libs/androidfw/LocaleDataLookup.cpp
index 6e751a77f355..ea9e9a2d4280 100644
--- a/libs/androidfw/LocaleDataLookup.cpp
+++ b/libs/androidfw/LocaleDataLookup.cpp
@@ -7518,6 +7518,13 @@ const char* lookupLikelyScript(uint32_t packed_lang_region) {
}
}
+/*
+ * TODO: Consider turning the below switch statement into binary search
+ * to save the disk space when the table is larger in the future.
+ * Disassembled code shows that the jump table emitted by clang can be
+ * 4x larger than the data in disk size, but it depends on the optimization option.
+ * However, a switch statement will benefit from the future of compiler improvement.
+ */
bool isLocaleRepresentative(uint32_t language_and_region, const char* script) {
const uint64_t packed_locale =
((static_cast<uint64_t>(language_and_region)) << 32u) |
diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp
index 970463492c1a..2a20106b6ee1 100644
--- a/libs/androidfw/TypeWrappers.cpp
+++ b/libs/androidfw/TypeWrappers.cpp
@@ -18,8 +18,11 @@
namespace android {
-TypeVariant::TypeVariant(const ResTable_type* data) : data(data), mLength(dtohl(data->entryCount)) {
- if (data->flags & ResTable_type::FLAG_SPARSE) {
+TypeVariant::TypeVariant(const ResTable_type* data)
+ : data(data)
+ , mLength(dtohl(data->entryCount))
+ , mSparse(data->flags & ResTable_type::FLAG_SPARSE) {
+ if (mSparse) {
const uint32_t entryCount = dtohl(data->entryCount);
const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(data) + dtohl(data->header.size);
const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
@@ -40,18 +43,18 @@ TypeVariant::iterator& TypeVariant::iterator::operator++() {
mIndex = mTypeVariant->mLength;
}
- const ResTable_type* type = mTypeVariant->data;
- if ((type->flags & ResTable_type::FLAG_SPARSE) == 0) {
+ if (!mTypeVariant->mSparse) {
return *this;
}
// Need to adjust |mSparseIndex| as well if we've passed its current element.
+ const ResTable_type* type = mTypeVariant->data;
const uint32_t entryCount = dtohl(type->entryCount);
- const auto entryIndices = reinterpret_cast<const uint32_t*>(
- reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
if (mSparseIndex >= entryCount) {
return *this; // done
}
+ const auto entryIndices = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
const auto element = (const ResTable_sparseTypeEntry*)(entryIndices + mSparseIndex);
if (mIndex > dtohs(element->idx)) {
++mSparseIndex;
@@ -79,7 +82,7 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
}
uint32_t entryOffset;
- if (type->flags & ResTable_type::FLAG_SPARSE) {
+ if (mTypeVariant->mSparse) {
if (mSparseIndex >= entryCount) {
return nullptr;
}
diff --git a/libs/androidfw/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h
index db641b78a4e4..d901af3c908b 100644
--- a/libs/androidfw/include/androidfw/TypeWrappers.h
+++ b/libs/androidfw/include/androidfw/TypeWrappers.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __TYPE_WRAPPERS_H
-#define __TYPE_WRAPPERS_H
+#pragma once
#include <androidfw/ResourceTypes.h>
#include <utils/ByteOrder.h>
@@ -54,7 +53,7 @@ struct TypeVariant {
enum class Kind { Begin, End };
iterator(const TypeVariant* tv, Kind kind)
: mTypeVariant(tv) {
- mSparseIndex = mIndex = kind == Kind::Begin ? 0 : tv->mLength;
+ mSparseIndex = mIndex = (kind == Kind::Begin ? 0 : tv->mLength);
// mSparseIndex here is technically past the number of sparse entries, but it is still
// ok as it is enough to infer that this is the end iterator.
}
@@ -75,9 +74,11 @@ struct TypeVariant {
const ResTable_type* data;
private:
- size_t mLength;
+ // For a dense table, this is the number of the elements.
+ // For a sparse table, this is the index of the last element + 1.
+ // In both cases, it can be used for iteration as the upper loop bound as in |i < mLength|.
+ uint32_t mLength;
+ bool mSparse;
};
} // namespace android
-
-#endif // __TYPE_WRAPPERS_H
diff --git a/libs/androidfw/tests/LocaleDataLookup_bench.cpp b/libs/androidfw/tests/LocaleDataLookup_bench.cpp
new file mode 100644
index 000000000000..60ce3b944551
--- /dev/null
+++ b/libs/androidfw/tests/LocaleDataLookup_bench.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#include "benchmark/benchmark.h"
+
+#include "androidfw/LocaleDataLookup.h"
+
+namespace android {
+
+static void BM_LocaleDataLookupIsLocaleRepresentative(benchmark::State& state) {
+ for (auto&& _ : state) {
+ isLocaleRepresentative(packLocale("en", "US"), "Latn");
+ isLocaleRepresentative(packLocale("es", "ES"), "Latn");
+ isLocaleRepresentative(packLocale("zh", "CN"), "Hans");
+ isLocaleRepresentative(packLocale("pt", "BR"), "Latn");
+ isLocaleRepresentative(packLocale("ar", "EG"), "Arab");
+ isLocaleRepresentative(packLocale("hi", "IN"), "Deva");
+ isLocaleRepresentative(packLocale("jp", "JP"), "Jpan");
+ }
+}
+BENCHMARK(BM_LocaleDataLookupIsLocaleRepresentative);
+
+static void BM_LocaleDataLookupLikelyScript(benchmark::State& state) {
+ for (auto&& _ : state) {
+ lookupLikelyScript(packLocale("en", ""));
+ lookupLikelyScript(packLocale("es", ""));
+ lookupLikelyScript(packLocale("zh", ""));
+ lookupLikelyScript(packLocale("pt", ""));
+ lookupLikelyScript(packLocale("ar", ""));
+ lookupLikelyScript(packLocale("hi", ""));
+ lookupLikelyScript(packLocale("jp", ""));
+ lookupLikelyScript(packLocale("en", "US"));
+ lookupLikelyScript(packLocale("es", "ES"));
+ lookupLikelyScript(packLocale("zh", "CN"));
+ lookupLikelyScript(packLocale("pt", "BR"));
+ lookupLikelyScript(packLocale("ar", "EG"));
+ lookupLikelyScript(packLocale("hi", "IN"));
+ lookupLikelyScript(packLocale("jp", "JP"));
+ }
+}
+BENCHMARK(BM_LocaleDataLookupLikelyScript);
+
+
+} // namespace android
diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp
index d66e05805484..69c24c5d8956 100644
--- a/libs/androidfw/tests/TypeWrappers_test.cpp
+++ b/libs/androidfw/tests/TypeWrappers_test.cpp
@@ -121,6 +121,7 @@ TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
values.push_back(std::nullopt);
values.push_back(std::nullopt);
values.push_back(Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x87654321});
+ values.push_back(std::nullopt);
// test for combinations of compact_entry and short_offsets
for (size_t i = 0; i < 8; i++) {
@@ -191,6 +192,17 @@ TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
++iter;
+ ASSERT_EQ(uint32_t(9), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ if (sparse) {
+ // Sparse iterator doesn't know anything beyond the last entry.
+ ASSERT_EQ(v.endEntries(), iter);
+ } else {
+ ASSERT_NE(v.endEntries(), iter);
+ }
+
+ ++iter;
+
ASSERT_EQ(v.endEntries(), iter);
}
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 6c96279711d0..ac53dcb1982a 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -2013,3 +2013,10 @@ flag {
description: "Show a Locked by your watch indicator on the keyguard when the device is locked by the watch."
bug: "387322459"
}
+
+flag {
+ name: "decouple_view_controller_in_animlib"
+ namespace: "systemui"
+ description: "Decouple view and controller in AnimLib."
+ bug: "393241010"
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index 103a9b5cf5f4..c5d2802c8941 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -319,7 +319,7 @@ internal class ExpandableControllerImpl(
override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
delegate.onTransitionAnimationStart(isExpandingFullyAbove)
- overlay.value = composeViewRoot.rootView.overlay as ViewGroupOverlay
+ overlay.value = transitionContainer.overlay as ViewGroupOverlay
cujType?.let { InteractionJankMonitor.getInstance().begin(composeViewRoot, it) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index eb1f1d9c52f4..5c983656225e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -51,6 +51,7 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -78,6 +79,32 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
setKeyguardFeaturesDisabled(SECONDARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_FEATURES_NONE)
+
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault,
+ false,
+ )
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault,
+ false,
+ )
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault,
+ false,
+ )
+ }
+
+ @After
+ fun tearDown() {
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault
+ )
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault
+ )
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault
+ )
}
@EnableFlags(FLAG_COMMUNAL_HUB)
@@ -334,6 +361,18 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
}
@Test
+ fun whenToDream_charging_defaultValue() =
+ kosmos.runTest {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault,
+ true,
+ )
+
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_CHARGING)
+ }
+
+ @Test
fun whenToDream_docked() =
kosmos.runTest {
val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
@@ -348,6 +387,18 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
}
@Test
+ fun whenToDream_docked_defaultValue() =
+ kosmos.runTest {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault,
+ true,
+ )
+
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_DOCKED)
+ }
+
+ @Test
fun whenToDream_postured() =
kosmos.runTest {
val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
@@ -362,6 +413,18 @@ class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiT
}
@Test
+ fun whenToDream_postured_defaultValue() =
+ kosmos.runTest {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault,
+ true,
+ )
+
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_POSTURED)
+ }
+
+ @Test
fun whenToDream_default() =
kosmos.runTest {
val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index ff137b768b65..c3cc3e66f81f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -21,6 +21,7 @@ import android.app.admin.DevicePolicyManager
import android.app.admin.devicePolicyManager
import android.content.Intent
import android.content.pm.UserInfo
+import android.content.res.mainResources
import android.os.UserHandle
import android.os.UserManager
import android.os.userManager
@@ -85,6 +86,7 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.advanceTimeBy
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -106,7 +108,10 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
private val secondaryUser = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
- private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val kosmos =
+ testKosmos()
+ .apply { mainResources = mContext.orCreateTestableResources.resources }
+ .useUnconfinedTestDispatcher()
private val Kosmos.underTest by Kosmos.Fixture { communalInteractor }
@@ -122,6 +127,32 @@ class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
+
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault,
+ false,
+ )
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault,
+ false,
+ )
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault,
+ false,
+ )
+ }
+
+ @After
+ fun tearDown() {
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault
+ )
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault
+ )
+ mContext.orCreateTestableResources.removeOverride(
+ com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault
+ )
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/DumpHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/DumpHandlerTest.kt
index 597629219634..09c4231b7178 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/DumpHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dump/DumpHandlerTest.kt
@@ -275,7 +275,9 @@ class DumpHandlerTest : SysuiTestCase() {
@Test
fun testDumpAllProtoDumpables() {
+ @Suppress("DEPRECATION")
dumpManager.registerDumpable("protoDumpable1", protoDumpable1)
+ @Suppress("DEPRECATION")
dumpManager.registerDumpable("protoDumpable2", protoDumpable2)
val args = arrayOf(DumpHandler.PROTO)
@@ -287,7 +289,9 @@ class DumpHandlerTest : SysuiTestCase() {
@Test
fun testDumpSingleProtoDumpable() {
+ @Suppress("DEPRECATION")
dumpManager.registerDumpable("protoDumpable1", protoDumpable1)
+ @Suppress("DEPRECATION")
dumpManager.registerDumpable("protoDumpable2", protoDumpable2)
val args = arrayOf(DumpHandler.PROTO, "protoDumpable1")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
index 6899d23bcfd7..e126b4d7265c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorParameterizedTest.kt
@@ -57,7 +57,6 @@ import platform.test.runner.parameterized.Parameters
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-@kotlinx.coroutines.ExperimentalCoroutinesApi
class KeyboardTouchpadEduInteractorParameterizedTest(private val gestureType: GestureType) :
SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
index dc393c01dce1..adcd849db49a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt
@@ -50,7 +50,6 @@ import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
-@kotlinx.coroutines.ExperimentalCoroutinesApi
class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt b/packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt
index 3a9a238d90a6..cadef52ce4cc 100644
--- a/packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt
@@ -41,7 +41,7 @@ class BootCompleteCacheImpl @Inject constructor(dumpManager: DumpManager) :
}
init {
- dumpManager.registerDumpable(TAG, this)
+ dumpManager.registerNormalDumpable(TAG, this)
}
@GuardedBy("listeners")
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index 21569c13fab4..64a46c0d7f00 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -131,7 +131,7 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
override fun onViewAttached() {
dialogManager.registerListener(dialogListener)
- dumpManager.registerDumpable(dumpTag, this)
+ dumpManager.registerNormalDumpable(dumpTag, this)
udfpsOverlayInteractor.setHandleTouches(shouldHandle = !shouldPauseAuth())
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index abd101693b43..4c291a0c5a2e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -111,6 +111,18 @@ constructor(
@Named(DEFAULT_BACKGROUND_TYPE) private val defaultBackgroundType: CommunalBackgroundType,
) : CommunalSettingsRepository {
+ private val dreamsActivatedOnSleepByDefault by lazy {
+ resources.getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault)
+ }
+
+ private val dreamsActivatedOnDockByDefault by lazy {
+ resources.getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault)
+ }
+
+ private val dreamsActivatedOnPosturedByDefault by lazy {
+ resources.getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault)
+ }
+
override fun getFlagEnabled(): Boolean {
return if (getV2FlagEnabled()) {
true
@@ -178,27 +190,27 @@ constructor(
.emitOnStart()
.map {
if (
- secureSettings.getIntForUser(
+ secureSettings.getBoolForUser(
Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
- 0,
+ dreamsActivatedOnSleepByDefault,
user.id,
- ) == 1
+ )
) {
WhenToDream.WHILE_CHARGING
} else if (
- secureSettings.getIntForUser(
+ secureSettings.getBoolForUser(
Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
- 0,
+ dreamsActivatedOnDockByDefault,
user.id,
- ) == 1
+ )
) {
WhenToDream.WHILE_DOCKED
} else if (
- secureSettings.getIntForUser(
+ secureSettings.getBoolForUser(
Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
- 0,
+ dreamsActivatedOnPosturedByDefault,
user.id,
- ) == 1
+ )
) {
WhenToDream.WHILE_POSTURED
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index a5f29aa658be..f3eff607dedd 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -122,7 +122,7 @@ constructor(
init {
Log.d(TAG, "Initializing")
- dumpManager.registerDumpable(TAG, this)
+ dumpManager.registerNormalDumpable(TAG, this)
serviceListing.addCallback(serviceListingCallback)
serviceListing.setListening(true)
serviceListing.reload()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index aa28c5b90090..a9729eaad266 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -124,7 +124,7 @@ constructor(
init {
legacySettingSyncer.startSyncing()
- dumpManager.registerDumpable("KeyguardQuickAffordances", Dumpster())
+ dumpManager.registerNormalDumpable("KeyguardQuickAffordances", Dumpster())
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
index 3eac12d2b24c..ea5f81c75405 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
@@ -306,7 +306,7 @@ class LegacyMediaDataManagerImpl(
}
init {
- dumpManager.registerDumpable(TAG, this)
+ dumpManager.registerNormalDumpable(TAG, this)
// Initialize the internal processing pipeline. The listeners at the front of the pipeline
// are set as internal listeners so that they receive events. From there, events are
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
index ad84a5eb74ab..0a70e78bf039 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
@@ -141,7 +141,7 @@ constructor(
init {
if (useMediaResumption) {
- dumpManager.registerDumpable(TAG, this)
+ dumpManager.registerNormalDumpable(TAG, this)
val unlockFilter = IntentFilter()
unlockFilter.addAction(Intent.ACTION_USER_UNLOCKED)
broadcastDispatcher.registerReceiver(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index f05029b2d55f..c604bfb2d5c1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -343,7 +343,7 @@ constructor(
.stateIn(applicationScope, SharingStarted.Eagerly, true)
init {
- dumpManager.registerDumpable(TAG, this)
+ dumpManager.registerNormalDumpable(TAG, this)
mediaFrame = inflateMediaCarousel()
mediaCarousel = mediaFrame.requireViewById(R.id.media_carousel_scroller)
pageIndicator = mediaFrame.requireViewById(R.id.media_page_indicator)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
index 67d390d4f10d..79a513f24995 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
@@ -85,7 +85,7 @@ class PrivacyConfig @Inject constructor(
}
init {
- dumpManager.registerDumpable(TAG, this)
+ dumpManager.registerNormalDumpable(TAG, this)
deviceConfigProxy.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_PRIVACY,
uiExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index eb8ef9bf3e50..f9a7205c56f5 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -98,7 +98,7 @@ class PrivacyItemController @Inject constructor(
}
init {
- dumpManager.registerDumpable(TAG, this)
+ dumpManager.registerNormalDumpable(TAG, this)
privacyConfig.addCallback(optionsCallback)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index e36e40d312b5..3604c4cc4cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -32,7 +32,6 @@ import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.SceneContainerTransitions
import com.android.systemui.scene.ui.viewmodel.SplitEdgeDetector
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -90,29 +89,14 @@ interface KeyguardlessSceneContainerFrameworkModule {
@Provides
fun containerConfig(): SceneContainerConfig {
return SceneContainerConfig(
- // Note that this list is in z-order. The first one is the bottom-most and the
- // last one is top-most.
- sceneKeys =
- listOfNotNull(
- Scenes.Gone,
- Scenes.QuickSettings.takeUnless { DualShade.isEnabled },
- Scenes.Shade.takeUnless { DualShade.isEnabled },
- ),
+ // Note that this list is in z-order. The first one is the bottom-most and the last
+ // one is top-most.
+ sceneKeys = listOf(Scenes.Gone, Scenes.QuickSettings, Scenes.Shade),
initialSceneKey = Scenes.Gone,
transitions = SceneContainerTransitions,
- overlayKeys =
- listOfNotNull(
- Overlays.NotificationsShade.takeIf { DualShade.isEnabled },
- Overlays.QuickSettingsShade.takeIf { DualShade.isEnabled },
- ),
+ overlayKeys = listOf(Overlays.NotificationsShade, Overlays.QuickSettingsShade),
navigationDistances =
- mapOf(
- Scenes.Gone to 0,
- Scenes.Shade to 1.takeUnless { DualShade.isEnabled },
- Scenes.QuickSettings to 2.takeUnless { DualShade.isEnabled },
- )
- .filterValues { it != null }
- .mapValues { checkNotNull(it.value) },
+ mapOf(Scenes.Gone to 0, Scenes.Shade to 1, Scenes.QuickSettings to 2),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index fe014524e3da..91677b03d589 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -32,7 +32,6 @@ import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.SceneContainerTransitions
import com.android.systemui.scene.ui.viewmodel.SplitEdgeDetector
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -103,28 +102,24 @@ interface SceneContainerFrameworkModule {
Scenes.Dream,
Scenes.Lockscreen,
Scenes.Bouncer,
- Scenes.QuickSettings.takeUnless { DualShade.isEnabled },
- Scenes.Shade.takeUnless { DualShade.isEnabled },
+ Scenes.QuickSettings,
+ Scenes.Shade,
),
initialSceneKey = Scenes.Lockscreen,
transitions = SceneContainerTransitions,
overlayKeys =
- listOfNotNull(
- Overlays.NotificationsShade.takeIf { DualShade.isEnabled },
- Overlays.QuickSettingsShade.takeIf { DualShade.isEnabled },
- ),
+ listOfNotNull(Overlays.NotificationsShade, Overlays.QuickSettingsShade),
navigationDistances =
mapOf(
Scenes.Gone to 0,
Scenes.Lockscreen to 0,
Scenes.Communal to 1,
Scenes.Dream to 2,
- Scenes.Shade to 3.takeUnless { DualShade.isEnabled },
- Scenes.QuickSettings to 4.takeUnless { DualShade.isEnabled },
+ Scenes.Shade to 3,
+ Scenes.QuickSettings to 4,
Scenes.Bouncer to 5,
)
- .filterValues { it != null }
- .mapValues { checkNotNull(it.value) },
+ .mapValues { it.value },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index a48d4d4d3b5f..1f02d5a2d0bc 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -151,7 +151,7 @@ internal constructor(
registerUserSwitchObserver()
- dumpManager.registerDumpable(TAG, this)
+ dumpManager.registerNormalDumpable(TAG, this)
}
override fun onReceive(context: Context, intent: Intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
index e38e53d67f61..e5349deaa811 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
@@ -43,7 +43,7 @@ constructor(
shadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged)
onPanelExpansionChanged(currentState)
shadeExpansionStateManager.addStateListener(this::onPanelStateChanged)
- dumpManager.registerDumpable(
+ dumpManager.registerNormalDumpable(
ScrimShadeTransitionController::class.java.simpleName,
this::dump
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProvider.kt
index 7358c513eaff..9d5d87f6db7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProvider.kt
@@ -185,7 +185,7 @@ constructor(
override fun start() {
configurationController.addCallback(this)
- dumpManager.registerDumpable(dumpableName, this)
+ dumpManager.registerNormalDumpable(dumpableName, this)
commandRegistry.registerCommand(commandName) {
StatusBarInsetsCommand(
object : StatusBarInsetsCommand.Callback {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index bac2c47f51c7..f822ee97807e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -95,7 +95,6 @@ import org.mockito.kotlin.clearInvocations
@RunWith(AndroidJUnit4::class)
@SmallTest
-@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
class ClockEventControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
index 4e0ebae6a902..c6ede0c8c602 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
@@ -36,7 +36,6 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.FieldSetter
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -55,7 +54,6 @@ import org.mockito.junit.MockitoRule
* can't mock the AccessibilityShortcutInfo for test. MultiValentTest doesn't compile when using
* newly introduced methods and constants.
*/
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class AccessibilityQsShortcutsRepositoryImplForDeviceTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 4baca713e19f..5249620dbdd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -63,7 +63,6 @@ import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.google.common.truth.Truth.assertThat
import dagger.Lazy
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -89,7 +88,6 @@ private const val DISPLAY_HEIGHT = 1920
private const val SENSOR_WIDTH = 30
private const val SENSOR_HEIGHT = 60
-@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt
index 655b2cc2dece..f3aaa3d3c7aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingButtonViewModelTest.kt
@@ -32,7 +32,6 @@ import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runCurrent
@@ -43,7 +42,6 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt
index e61acc4e1d0b..9ae57153f3ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingDeviceItemActionInteractorTest.kt
@@ -33,7 +33,6 @@ import com.android.systemui.plugins.activityStarter
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.After
@@ -56,7 +55,6 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@OptIn(ExperimentalCoroutinesApi::class)
class AudioSharingDeviceItemActionInteractorTest : SysuiTestCase() {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
index a56c2cb25542..242d31a26b13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
@@ -45,7 +45,6 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -67,7 +66,6 @@ import org.mockito.junit.MockitoRule
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@OptIn(ExperimentalCoroutinesApi::class)
@EnableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
class BluetoothTileDialogViewModelTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderContentProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderContentProviderTest.kt
index 8d9fa6ad6e08..e50035d0067e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderContentProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderContentProviderTest.kt
@@ -41,7 +41,6 @@ const val AUTHORITY = "exception.provider.authority"
val TEST_URI = Uri.Builder().scheme("content").authority(AUTHORITY).path("path").build()
@SmallTest
-@kotlinx.coroutines.ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
class ImageLoaderContentProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
index 76c3349c25ba..de70bca7e96c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
@@ -29,7 +29,6 @@ import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
-@kotlinx.coroutines.ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
class ImageLoaderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index a3c3d2cdbb43..32b8bb49de96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -70,7 +70,6 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -84,7 +83,6 @@ import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index ab691c630f97..a17125388ed6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -59,7 +59,6 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@@ -79,7 +78,6 @@ import platform.test.runner.parameterized.Parameter
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@DisableSceneContainer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
index caf08efc4b32..2648c9c6496e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt
@@ -60,7 +60,6 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@@ -79,7 +78,6 @@ import platform.test.runner.parameterized.Parameter
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
-@OptIn(ExperimentalCoroutinesApi::class)
@FlakyTest(
bugId = 292574995,
detail = "on certain architectures all permutations with startActivity=true is causing failures",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index 418972055324..df24bff43c08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -38,7 +38,6 @@ import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import org.junit.Before
@@ -49,7 +48,6 @@ import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
-@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
@SmallTest
class DefaultDeviceEntrySectionTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 051aba3d593f..26c13f2a2519 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -77,7 +77,6 @@ import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth
import kotlin.math.min
import kotlin.test.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.map
@@ -90,7 +89,6 @@ import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
index e3aeaa85873d..fe5acb5668ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/RepeatWhenAttachedTest.kt
@@ -28,7 +28,6 @@ import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch
@@ -52,7 +51,6 @@ import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.KArgumentCaptor
import org.mockito.kotlin.argumentCaptor
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class RepeatWhenAttachedTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index 676d8fa06d82..122af0639030 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -73,7 +73,6 @@ import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent
@@ -117,7 +116,6 @@ private const val SESSION_EMPTY_TITLE = ""
private const val USER_ID = 0
private val DISMISS_INTENT = Intent().apply { action = "dismiss" }
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(ParameterizedAndroidJunit4::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 811d2e2b2b06..b731c4f18c7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -49,7 +49,6 @@ import com.android.systemui.testKosmos
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -81,7 +80,6 @@ private const val SMARTSPACE_KEY = "SMARTSPACE_KEY"
private const val SMARTSPACE_PACKAGE = "SMARTSPACE_PKG"
private val SMARTSPACE_INSTANCE_ID = InstanceId.fakeInstanceId(456)!!
-@ExperimentalCoroutinesApi
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 496b31990b9d..f9e8cbfc491c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -80,7 +80,6 @@ import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent
@@ -123,7 +122,6 @@ private const val SESSION_EMPTY_TITLE = ""
private const val USER_ID = 0
private val DISMISS_INTENT = Intent().apply { action = "dismiss" }
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(ParameterizedAndroidJunit4::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 6c8a46f19f2b..a2bd5ec28f08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -79,7 +79,6 @@ import java.util.Locale
import javax.inject.Provider
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -112,7 +111,6 @@ private val SMARTSPACE_KEY = "smartspace"
private const val PAUSED_LOCAL = "paused local"
private const val PLAYING_LOCAL = "playing local"
-@ExperimentalCoroutinesApi
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(ParameterizedAndroidJunit4::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 072caa74df20..6ca4ae2d2259 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -61,7 +61,6 @@ import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -86,7 +85,6 @@ import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.lastValue
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -458,7 +456,6 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isTrue()
}
- @OptIn(ExperimentalCoroutinesApi::class)
@Test
fun isCurrentlyInGuidedTransformation_hostsVisible_expandImmediateEnabled_returnsFalse() =
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 69539743f96f..042eb871e3c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -70,7 +70,6 @@ import com.android.wm.shell.bubbles.Bubbles
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlin.test.assertNotNull
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
@@ -90,7 +89,6 @@ import org.mockito.MockitoAnnotations
import org.mockito.kotlin.whenever
/** atest SystemUITests:NoteTaskControllerTest */
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
internal class NoteTaskControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 266cb51cc855..8a4f1ad13b78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -47,7 +47,6 @@ import com.android.systemui.util.time.FakeSystemClock
import com.android.wm.shell.bubbles.Bubbles
import com.google.common.truth.Truth.assertThat
import java.util.Optional
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -60,7 +59,7 @@ import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations.initMocks
/** atest SystemUITests:NoteTaskInitializerTest */
-@OptIn(ExperimentalCoroutinesApi::class, InternalNoteTaskApi::class)
+@OptIn(InternalNoteTaskApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
internal class NoteTaskInitializerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index b7fb759eb138..8d7de7e04a03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.notetask.quickaffordance
import android.app.role.RoleManager
@@ -48,7 +46,6 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runTest
import org.junit.After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
index c8faa81adffa..cf54df8565d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
@@ -45,7 +45,6 @@ import org.mockito.kotlin.whenever
/** atest SystemUITests:com.android.systemui.reardisplay.RearDisplayCoreStartableTest */
@SmallTest
-@kotlinx.coroutines.ExperimentalCoroutinesApi
class RearDisplayCoreStartableTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index f695c13a9e62..1474defa1662 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -38,7 +38,6 @@ import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.TruthJUnit.assume
import java.util.concurrent.Executor
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -58,7 +57,6 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(Parameterized::class)
class UserTrackerImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 6724f82dfd99..732561e0979b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -69,7 +69,6 @@ import com.android.systemui.statusbar.lockscreen.lockscreenSmartspaceController
import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -87,7 +86,6 @@ import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-@ExperimentalCoroutinesApi
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 49d6909c1f93..856ece7e7ff3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -89,7 +89,6 @@ import com.android.systemui.window.ui.viewmodel.WindowRootViewModel
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.TestScope
@@ -116,7 +115,6 @@ import org.mockito.kotlin.clearInvocations
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@RunWithLooper(setAsMainLooper = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index cfc00a918f61..b7040ee2a11e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -35,7 +35,6 @@ import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.After
@@ -69,7 +68,6 @@ private fun <T> anyObject(): T {
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalCoroutinesApi::class)
class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
index 98487f7ac059..f8154ddb2a5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
@@ -3,7 +3,6 @@ package com.android.systemui.statusbar
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
@@ -30,7 +29,6 @@ class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() {
private val configurationController = FakeConfigurationController()
- @OptIn(ExperimentalCoroutinesApi::class)
@Mock private lateinit var scrimController: ScrimController
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
private var qS: QS? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index 77ca51c5efcb..3c4aeccec2eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -41,7 +41,6 @@ import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.util.time.FakeSystemClock
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
@@ -64,7 +63,6 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
index c4ef4f978ff8..a16f2f6161ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.statusbar.notification.icon
import android.app.ActivityManager
@@ -43,7 +41,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 8645a40319f4..7cc1ac1d3806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -75,7 +75,6 @@ import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.wmshell.BubblesManager
import java.util.Optional
import kotlin.test.assertEquals
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runCurrent
import org.junit.Assert
@@ -98,7 +97,6 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
/** Tests for [NotificationGutsManager] with the scene container enabled. */
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
index 8beed01ffbe4..f4c0e367871f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
@@ -29,7 +29,6 @@ import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
@@ -39,7 +38,6 @@ import org.junit.runner.RunWith
import org.mockito.MockitoAnnotations
@SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@RunWith(AndroidJUnit4::class)
class AirplaneModeViewModelImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
index 4a2f6f281566..df74404aaaf8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
@@ -25,12 +25,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class SystemUiCarrierConfigTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImplTest.kt
index 7d101be4e748..d074fc256133 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryImplTest.kt
@@ -29,7 +29,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -45,7 +44,6 @@ import org.mockito.MockitoAnnotations
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class CarrierConfigRepositoryImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 36f5236c3936..d05590521fab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -50,7 +50,6 @@ import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
@@ -70,7 +69,6 @@ import org.mockito.MockitoAnnotations
* switches over when the value of `demoMode` changes
*/
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileRepositorySwitcherTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index fd23655ffc1c..02ad90cb4269 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -50,7 +50,6 @@ import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -69,7 +68,6 @@ import org.mockito.Mockito.verify
* properly switches over when the value of `isCarrierMerged` changes.
*/
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class FullMobileConnectionRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 3a25ecb27404..df5c6e916931 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -98,7 +98,6 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -115,7 +114,6 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileConnectionRepositoryTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index 0d82c79fea79..ec260fcc7a65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -49,7 +49,6 @@ import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityMod
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -92,7 +91,6 @@ import org.mockito.MockitoAnnotations
* 5. Assert that B has the state sent in step #2
*/
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
private lateinit var underTest: MobileConnectionRepositoryImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 6c60f55a3904..d1d6e27332b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -79,7 +79,6 @@ import com.android.wifitrackerlib.WifiEntry
import com.android.wifitrackerlib.WifiPickerTracker
import com.google.common.truth.Truth.assertThat
import java.util.UUID
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -101,7 +100,6 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
// This is required because our [SubscriptionManager.OnSubscriptionsChangedListener] uses a looper
// to run the callback and this makes the looper place nicely with TestScope etc.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index 84846a16f39a..ce99e595504d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -44,7 +44,6 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -57,7 +56,6 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
-@OptIn(ExperimentalCoroutinesApi::class)
class ModernStatusBarMobileViewTest : SysuiTestCase() {
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index 599729d953d4..6a8b783acf73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -53,7 +53,6 @@ import com.google.common.truth.Truth.assertThat
import java.util.Optional
import java.util.function.Consumer
import kotlin.test.Test
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -71,7 +70,6 @@ import org.mockito.MockitoAnnotations
import org.mockito.kotlin.doThrow
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class DeviceBasedSatelliteRepositoryImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
index 88f262bec123..8a0159ce174e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryImplTest.kt
@@ -47,7 +47,6 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.Connectivi
import com.android.systemui.testKosmos
import com.android.systemui.tuner.TunerService
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -60,7 +59,6 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class ConnectivityRepositoryImplTest : SysuiTestCase() {
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 383e75bb5122..e202d0ecfa23 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -151,6 +151,10 @@ android.os.Looper
android.os.Message
android.os.MessageQueue
android.os.MessageQueue_ravenwood
+android.os.PerfettoTrace
+android.os.PerfettoTrace$Category
+android.os.PerfettoTrackEventExtra
+android.os.PerfettoTrackEventExtra$NoOpBuilder
android.os.PackageTagsList
android.os.Parcel
android.os.ParcelFileDescriptor
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 40fb8b671d0c..8e448676c214 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -81,6 +81,7 @@ public class AutoclickController extends BaseEventStreamTransformation {
@VisibleForTesting AutoclickSettingsObserver mAutoclickSettingsObserver;
@VisibleForTesting AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
@VisibleForTesting AutoclickIndicatorView mAutoclickIndicatorView;
+ @VisibleForTesting AutoclickTypePanel mAutoclickTypePanel;
private WindowManager mWindowManager;
public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
@@ -123,6 +124,9 @@ public class AutoclickController extends BaseEventStreamTransformation {
mAutoclickIndicatorView = new AutoclickIndicatorView(mContext);
mWindowManager = mContext.getSystemService(WindowManager.class);
+ mAutoclickTypePanel = new AutoclickTypePanel(mContext, mWindowManager);
+
+ mAutoclickTypePanel.show();
mWindowManager.addView(mAutoclickIndicatorView, mAutoclickIndicatorView.getLayoutParams());
}
@@ -167,6 +171,7 @@ public class AutoclickController extends BaseEventStreamTransformation {
mAutoclickIndicatorScheduler.cancel();
mAutoclickIndicatorScheduler = null;
mWindowManager.removeView(mAutoclickIndicatorView);
+ mAutoclickTypePanel.hide();
}
}
@@ -500,6 +505,11 @@ public class AutoclickController extends BaseEventStreamTransformation {
mMetaState = state;
}
+ @VisibleForTesting
+ int getMetaStateForTesting() {
+ return mMetaState;
+ }
+
/**
* Updates delay that should be used when scheduling clicks. The delay will be used only for
* clicks scheduled after this point (pending click tasks are not affected).
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
new file mode 100644
index 000000000000..1ba574559918
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 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.server.accessibility.autoclick;
+
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+
+public class AutoclickTypePanel {
+
+ private final String TAG = AutoclickTypePanel.class.getSimpleName();
+
+ private final Context mContext;
+
+ private final View mContentView;
+
+ private final WindowManager mWindowManager;
+
+ public AutoclickTypePanel(Context context, WindowManager windowManager) {
+ mContext = context;
+ mWindowManager = windowManager;
+
+ mContentView = LayoutInflater.from(context).inflate(
+ R.layout.accessibility_autoclick_type_panel, null);
+ }
+
+ public void show() {
+ mWindowManager.addView(mContentView, getLayoutParams());
+ }
+
+ public void hide() {
+ mWindowManager.removeView(mContentView);
+ }
+
+ /**
+ * Retrieves the layout params for AutoclickIndicatorView, used when it's added to the Window
+ * Manager.
+ */
+ @NonNull
+ private WindowManager.LayoutParams getLayoutParams() {
+ final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
+ layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ layoutParams.setFitInsetsTypes(WindowInsets.Type.statusBars());
+ layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ layoutParams.format = PixelFormat.TRANSLUCENT;
+ layoutParams.setTitle(AutoclickTypePanel.class.getSimpleName());
+ layoutParams.accessibilityTitle =
+ mContext.getString(R.string.accessibility_autoclick_type_settings_panel_title);
+ layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ // TODO(b/388847771): Compute position based on user interaction.
+ layoutParams.x = 15;
+ layoutParams.y = 90;
+ layoutParams.gravity = Gravity.END | Gravity.BOTTOM;
+
+ return layoutParams;
+ }
+}
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 7a5b8660ef7c..cf0d7e72ba8b 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -53,6 +53,7 @@ import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.am.DropboxRateLimiter;
import com.android.server.os.TombstoneProtos.Tombstone;
+import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -154,6 +155,10 @@ public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
+ if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+ return;
+ }
+
// Log boot events in the background to avoid blocking the main thread with I/O
new Thread() {
@Override
@@ -219,6 +224,8 @@ public class BootReceiver extends BroadcastReceiver {
} catch (Exception e) {
Slog.wtf(TAG, "Error watching for trace events", e);
return 0; // Unregister the handler.
+ } finally {
+ IoUtils.closeQuietly(fd);
}
return OnFileDescriptorEventListener.EVENT_INPUT;
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 19e7e062758a..ce6e1ba0cfda 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -947,7 +947,6 @@ class StorageManagerService extends IStorageManager.Stub
refreshZramSettings();
if (mmdEnabled()) {
- // TODO: b/375432472 - Start zram maintenance only when zram is enabled.
ZramMaintenance.startZramMaintenance(mContext);
} else {
// Schedule zram writeback unless zram is disabled by persist.sys.zram_enabled
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 896c9b8d0932..c65981bf0703 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -16,11 +16,13 @@
package com.android.server;
-import static android.app.Flags.modesApi;
import static android.app.Flags.enableCurrentModeTypeBinderCache;
import static android.app.Flags.enableNightModeBinderCache;
+import static android.app.Flags.modesApi;
import static android.app.UiModeManager.ContrastUtils.CONTRAST_DEFAULT_VALUE;
import static android.app.UiModeManager.DEFAULT_PRIORITY;
+import static android.app.UiModeManager.FORCE_INVERT_TYPE_DARK;
+import static android.app.UiModeManager.FORCE_INVERT_TYPE_OFF;
import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
import static android.app.UiModeManager.MODE_NIGHT_AUTO;
import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
@@ -33,6 +35,7 @@ import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE;
import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
import static android.os.UserHandle.USER_SYSTEM;
import static android.os.UserHandle.getCallingUserId;
+import static android.provider.Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED;
import static android.provider.Settings.Secure.CONTRAST_LEVEL;
import static android.util.TimeUtils.isTimeBetween;
@@ -45,6 +48,7 @@ import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.ActivityThread;
import android.app.AlarmManager;
import android.app.IOnProjectionStateChangedListener;
import android.app.IUiModeManager;
@@ -56,6 +60,7 @@ import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.UiModeManager;
import android.app.UiModeManager.AttentionModeThemeOverlayType;
+import android.app.UiModeManager.ForceInvertType;
import android.app.UiModeManager.NightModeCustomReturnType;
import android.app.UiModeManager.NightModeCustomType;
import android.content.BroadcastReceiver;
@@ -93,6 +98,7 @@ import android.service.vr.IVrStateCallbacks;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -256,6 +262,9 @@ final class UiModeManagerService extends SystemService {
@GuardedBy("mLock")
private final SparseArray<Float> mContrasts = new SparseArray<>();
+ @GuardedBy("mLock")
+ private final SparseIntArray mForceInvertStates = new SparseIntArray();
+
public UiModeManagerService(Context context) {
this(context, /* setupWizardComplete= */ false, /* tm= */ null, new Injector());
}
@@ -407,9 +416,33 @@ final class UiModeManagerService extends SystemService {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateSystemProperties();
+ updateForceInvertStates();
}
};
+ private final ContentObserver mForceInvertStateObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateForceInvertStates();
+ }
+ };
+
+ private void updateForceInvertStates() {
+ if (!android.view.accessibility.Flags.forceInvertColor()) {
+ return;
+ }
+
+ synchronized (mLock) {
+ if (updateForceInvertStateLocked()) {
+ int forceInvertState = getForceInvertStateLocked();
+ mUiModeManagerCallbacks.get(mCurrentUser, new RemoteCallbackList<>())
+ .broadcast(ignoreRemoteException(
+ callback ->
+ callback.notifyForceInvertStateChanged(forceInvertState)));
+ }
+ }
+ }
+
private final ContentObserver mContrastObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
@@ -485,6 +518,12 @@ final class UiModeManagerService extends SystemService {
context.getContentResolver()
.registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE),
false, mDarkThemeObserver, 0);
+ if (android.view.accessibility.Flags.forceInvertColor()) {
+ context.getContentResolver()
+ .registerContentObserver(
+ Secure.getUriFor(ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED),
+ false, mForceInvertStateObserver, UserHandle.USER_ALL);
+ }
context.getContentResolver().registerContentObserver(
Secure.getUriFor(Secure.CONTRAST_LEVEL), false,
mContrastObserver, UserHandle.USER_ALL);
@@ -919,7 +958,7 @@ final class UiModeManagerService extends SystemService {
@android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE)
@Override
- public @NightModeCustomReturnType int getNightModeCustomType() {
+ public @NightModeCustomReturnType int getNightModeCustomType() {
getNightModeCustomType_enforcePermission();
synchronized (mLock) {
return mNightModeCustomType;
@@ -1275,6 +1314,14 @@ final class UiModeManagerService extends SystemService {
return getContrastLocked();
}
}
+
+ @Override
+ @ForceInvertType
+ public int getForceInvertState() {
+ synchronized (mLock) {
+ return getForceInvertStateLocked();
+ }
+ }
};
private void enforceProjectionTypePermissions(@UiModeManager.ProjectionType int p) {
@@ -1358,6 +1405,76 @@ final class UiModeManagerService extends SystemService {
}
/**
+ * Return the force invert for the current user. If not cached, fetch it from the settings.
+ */
+ @GuardedBy("mLock")
+ @ForceInvertType
+ private int getForceInvertStateLocked() {
+ if (mForceInvertStates.indexOfKey(mCurrentUser) < 0) {
+ updateForceInvertStateLocked();
+ }
+ return mForceInvertStates.get(mCurrentUser);
+ }
+
+ /**
+ * Read the force invert setting for the current user and update {@link #mForceInvertStates}
+ * if the contrast changed. Returns true if {@link #mForceInvertStates} was updated.
+ */
+ @GuardedBy("mLock")
+ private boolean updateForceInvertStateLocked() {
+ int forceInvertState = getForceInvertStateInternal();
+ if (mForceInvertStates.get(mCurrentUser) != forceInvertState) {
+ mForceInvertStates.put(mCurrentUser, forceInvertState);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the current state of force invert, which modifies the display colors to
+ * enhance visibility based on the system's dark theme settings and app-specific configurations.
+ *
+ * <p>This method is for informational purposes only. The application does not need to
+ * implement any special handling for force invert; the system applies it automatically.
+ * If you want to prevent force invert from affecting your app, ensure you have defined
+ * both light and dark themes. Force invert is not applied to apps that already adapt
+ * to the user's system theme preference.</p>
+ *
+ * @return The current force invert state, represented by a {@code ForceDarkType} constant.
+ *
+ * @hide
+ */
+ private int getForceInvertStateInternal() {
+ if (!android.view.accessibility.Flags.forceInvertColor()) {
+ return FORCE_INVERT_TYPE_OFF;
+ }
+
+ if (!isSystemInDarkTheme()) {
+ return FORCE_INVERT_TYPE_OFF;
+ }
+
+ if (!isForceInvert()) {
+ return FORCE_INVERT_TYPE_OFF;
+ }
+
+ return FORCE_INVERT_TYPE_DARK;
+ }
+
+ private boolean isSystemInDarkTheme() {
+ Context sysUiContext = ActivityThread.currentActivityThread().getSystemUiContext();
+ int sysUiNightMode = sysUiContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK;
+ return sysUiNightMode == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ private boolean isForceInvert() {
+ return Settings.Secure.getIntForUser(
+ getContext().getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+ /* def= */ 0, mCurrentUser) == 1;
+ }
+
+ /**
* Return the contrast for the current user. If not cached, fetch it from the settings.
*/
@GuardedBy("mLock")
@@ -1967,6 +2084,14 @@ final class UiModeManagerService extends SystemService {
sendConfigurationAndStartDreamOrDockAppLocked(category);
}
+ private boolean shouldStartDockApp(Context context, Intent homeIntent) {
+ if (mWatch && !mSetupWizardComplete) {
+ // Do not ever start dock app when setup is not complete on a watch.
+ return false;
+ }
+ return Sandman.shouldStartDockApp(context, homeIntent);
+ }
+
private void sendConfigurationAndStartDreamOrDockAppLocked(String category) {
// Update the configuration but don't send it yet.
mHoldingConfiguration = false;
@@ -1983,7 +2108,7 @@ final class UiModeManagerService extends SystemService {
// activity manager take care of both the start and config
// change.
Intent homeIntent = buildHomeIntent(category);
- if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
+ if (shouldStartDockApp(getContext(), homeIntent)) {
try {
int result = ActivityTaskManager.getService().startActivityWithConfig(
null, getContext().getBasePackageName(),
diff --git a/services/core/java/com/android/server/ZramMaintenance.java b/services/core/java/com/android/server/ZramMaintenance.java
index cdb48122e321..099e5b3fe440 100644
--- a/services/core/java/com/android/server/ZramMaintenance.java
+++ b/services/core/java/com/android/server/ZramMaintenance.java
@@ -24,11 +24,15 @@ import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
import android.os.IMmd;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.provider.DeviceConfig;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.time.Duration;
/**
@@ -46,43 +50,45 @@ public class ZramMaintenance extends JobService {
private static final String TAG = ZramMaintenance.class.getName();
// Job id must be unique across all clients of the same uid. ZramMaintenance uses the bug number
// as the job id.
- private static final int JOB_ID = 375432472;
+ @VisibleForTesting
+ public static final int JOB_ID = 375432472;
private static final ComponentName sZramMaintenance =
new ComponentName("android", ZramMaintenance.class.getName());
+ @VisibleForTesting
+ public static final String KEY_CHECK_STATUS = "check_status";
+ private static final String SYSTEM_PROPERTY_PREFIX = "mm.";
private static final String FIRST_DELAY_SECONDS_PROP =
- "mm.zram.maintenance.first_delay_seconds";
+ "zram.maintenance.first_delay_seconds";
// The default is 1 hour.
private static final long DEFAULT_FIRST_DELAY_SECONDS = 3600;
private static final String PERIODIC_DELAY_SECONDS_PROP =
- "mm.zram.maintenance.periodic_delay_seconds";
+ "zram.maintenance.periodic_delay_seconds";
// The default is 1 hour.
private static final long DEFAULT_PERIODIC_DELAY_SECONDS = 3600;
private static final String REQUIRE_DEVICE_IDLE_PROP =
- "mm.zram.maintenance.require_device_idle";
+ "zram.maintenance.require_device_idle";
private static final boolean DEFAULT_REQUIRE_DEVICE_IDLE =
true;
private static final String REQUIRE_BATTERY_NOT_LOW_PROP =
- "mm.zram.maintenance.require_battry_not_low";
+ "zram.maintenance.require_battry_not_low";
private static final boolean DEFAULT_REQUIRE_BATTERY_NOT_LOW =
true;
@Override
public boolean onStartJob(JobParameters params) {
- IBinder binder = ServiceManager.getService("mmd");
- if (binder != null) {
- IMmd mmd = IMmd.Stub.asInterface(binder);
- try {
- mmd.doZramMaintenance();
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to doZramMaintenance", e);
+ new Thread("ZramMaintenance") {
+ @Override
+ public void run() {
+ try {
+ IBinder binder = ServiceManager.getService("mmd");
+ IMmd mmd = IMmd.Stub.asInterface(binder);
+ startJob(ZramMaintenance.this, params, mmd);
+ } finally {
+ jobFinished(params, false);
+ }
}
- } else {
- Slog.w(TAG, "binder not found");
- }
- Duration delay = Duration.ofSeconds(SystemProperties.getLong(PERIODIC_DELAY_SECONDS_PROP,
- DEFAULT_PERIODIC_DELAY_SECONDS));
- scheduleZramMaintenance(this, delay);
+ }.start();
return true;
}
@@ -92,27 +98,75 @@ public class ZramMaintenance extends JobService {
}
/**
+ * This is public to test ZramMaintenance logic.
+ *
+ * <p>
+ * We need to pass mmd as parameter because we can't mock "IMmd.Stub.asInterface".
+ *
+ * <p>
+ * Since IMmd.isZramMaintenanceSupported() is blocking call, this method should be executed on
+ * a worker thread.
+ */
+ @VisibleForTesting
+ public static void startJob(Context context, JobParameters params, IMmd mmd) {
+ boolean checkStatus = params.getExtras().getBoolean(KEY_CHECK_STATUS);
+ if (mmd != null) {
+ try {
+ if (checkStatus && !mmd.isZramMaintenanceSupported()) {
+ Slog.i(TAG, "zram maintenance is not supported");
+ return;
+ }
+ // Status check is required before the first doZramMaintenanceAsync() call once.
+ checkStatus = false;
+
+ mmd.doZramMaintenanceAsync();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to binder call to mmd", e);
+ }
+ } else {
+ Slog.w(TAG, "binder not found");
+ }
+ Duration delay = Duration.ofSeconds(getLongProperty(PERIODIC_DELAY_SECONDS_PROP,
+ DEFAULT_PERIODIC_DELAY_SECONDS));
+ scheduleZramMaintenance(context, delay, checkStatus);
+ }
+
+ /**
* Starts periodical zram maintenance.
*/
public static void startZramMaintenance(Context context) {
Duration delay = Duration.ofSeconds(
- SystemProperties.getLong(FIRST_DELAY_SECONDS_PROP, DEFAULT_FIRST_DELAY_SECONDS));
- scheduleZramMaintenance(context, delay);
+ getLongProperty(FIRST_DELAY_SECONDS_PROP, DEFAULT_FIRST_DELAY_SECONDS));
+ scheduleZramMaintenance(context, delay, true);
}
- private static void scheduleZramMaintenance(Context context, Duration delay) {
+ private static void scheduleZramMaintenance(Context context, Duration delay,
+ boolean checkStatus) {
JobScheduler js = context.getSystemService(JobScheduler.class);
if (js != null) {
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(KEY_CHECK_STATUS, checkStatus);
js.schedule(new JobInfo.Builder(JOB_ID, sZramMaintenance)
.setMinimumLatency(delay.toMillis())
.setRequiresDeviceIdle(
- SystemProperties.getBoolean(REQUIRE_DEVICE_IDLE_PROP,
+ getBooleanProperty(REQUIRE_DEVICE_IDLE_PROP,
DEFAULT_REQUIRE_DEVICE_IDLE))
.setRequiresBatteryNotLow(
- SystemProperties.getBoolean(REQUIRE_BATTERY_NOT_LOW_PROP,
+ getBooleanProperty(REQUIRE_BATTERY_NOT_LOW_PROP,
DEFAULT_REQUIRE_BATTERY_NOT_LOW))
+ .setExtras(bundle)
.build());
}
}
+
+ private static long getLongProperty(String name, long defaultValue) {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_MM, name,
+ SystemProperties.getLong(SYSTEM_PROPERTY_PREFIX + name, defaultValue));
+ }
+
+ private static boolean getBooleanProperty(String name, boolean defaultValue) {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_MM, name,
+ SystemProperties.getBoolean(SYSTEM_PROPERTY_PREFIX + name, defaultValue));
+ }
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e8a222625b1b..32c4e9b1727e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -551,6 +551,11 @@ public class AppOpsService extends IAppOpsService.Stub {
@VisibleForTesting
final Constants mConstants;
+ /**
+ * Some processes in the user may still be running when trying to drop the user's state
+ */
+ private static final long REMOVE_USER_DELAY = 5000L;
+
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
final class UidState {
public final int uid;
@@ -6820,14 +6825,17 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void removeUser(int userHandle) throws RemoteException {
checkSystemUid("removeUser");
- synchronized (AppOpsService.this) {
- final int tokenCount = mOpUserRestrictions.size();
- for (int i = tokenCount - 1; i >= 0; i--) {
- ClientUserRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i);
- opRestrictions.removeUser(userHandle);
+ mHandler.postDelayed(() -> {
+ Slog.i(TAG, "Removing user " + userHandle + " from AppOpsService");
+ synchronized (AppOpsService.this) {
+ final int tokenCount = mOpUserRestrictions.size();
+ for (int i = tokenCount - 1; i >= 0; i--) {
+ ClientUserRestrictionState opRestrictions = mOpUserRestrictions.valueAt(i);
+ opRestrictions.removeUser(userHandle);
+ }
+ removeUidsForUserLocked(userHandle);
}
- removeUidsForUserLocked(userHandle);
- }
+ }, REMOVE_USER_DELAY);
}
@Override
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index fb5ce5b4e5fa..41f58ae76a4d 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -809,7 +809,7 @@ final class KeyGestureController {
if (firstDown) {
handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_FULLSCREEN},
/* modifierState = */0,
- KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken,
/* flags = */0, /* appLaunchData = */null);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 8710438d76b3..d6b587b5f16d 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -251,14 +251,17 @@ public final class OverlayManagerService extends SystemService {
private final Object mLock = new Object();
+ @GuardedBy("mLock")
private final AtomicFile mSettingsFile;
private final PackageManagerHelperImpl mPackageManager;
private final UserManagerService mUserManager;
+ @GuardedBy("mLock")
private final OverlayManagerSettings mSettings;
+ @GuardedBy("mLock")
private final OverlayManagerServiceImpl mImpl;
private final OverlayActorEnforcer mActorEnforcer;
@@ -296,7 +299,9 @@ public final class OverlayManagerService extends SystemService {
UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
umi.addUserLifecycleListener(new UserLifecycleListener());
- restoreSettings();
+ // No async stuff happening in the constructor yet, so it's OK to call a ...Locked()
+ // method without a lock here.
+ restoreSettingsLocked();
// Wipe all shell overlays on boot, to recover from a potentially broken device
String shellPkgName = TextUtils.emptyIfNull(
@@ -403,6 +408,20 @@ public final class OverlayManagerService extends SystemService {
return userIds;
}
+ /**
+ * Ensure that the caller has permission to interact with the given userId.
+ * If the calling user is not the same as the provided user, the caller needs
+ * to hold the INTERACT_ACROSS_USERS_FULL permission (or be system uid or
+ * root).
+ *
+ * @param userId the user to interact with
+ * @param message message for any SecurityException
+ */
+ static int handleIncomingUser(final int userId, @NonNull final String message) {
+ return ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, message, null);
+ }
+
private void handlePackageAdd(String packageName, Bundle extras, int userId) {
final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
if (replacing) {
@@ -902,25 +921,28 @@ public final class OverlayManagerService extends SystemService {
throws RemoteException {
try {
traceBegin(TRACE_TAG_RRO, "OMS#commit " + transaction);
- try {
- executeAllRequests(transaction);
- } catch (Exception e) {
- final long ident = Binder.clearCallingIdentity();
+ synchronized (mLock) {
try {
- restoreSettings();
- } finally {
- Binder.restoreCallingIdentity(ident);
+ executeAllRequestsLocked(transaction);
+ } catch (Exception e) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ restoreSettingsLocked();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ Slog.d(TAG, "commit failed: " + e.getMessage(), e);
+ throw new SecurityException("commit failed"
+ + (DEBUG || Build.IS_DEBUGGABLE ? ": " + e.getMessage() : ""));
}
- Slog.d(TAG, "commit failed: " + e.getMessage(), e);
- throw new SecurityException("commit failed"
- + (DEBUG || Build.IS_DEBUGGABLE ? ": " + e.getMessage() : ""));
}
} finally {
traceEnd(TRACE_TAG_RRO);
}
}
- private Set<UserPackage> executeRequest(
+ @GuardedBy("mLock")
+ private Set<UserPackage> executeRequestLocked(
@NonNull final OverlayManagerTransaction.Request request)
throws OperationFailedException {
Objects.requireNonNull(request, "Transaction contains a null request");
@@ -995,33 +1017,29 @@ public final class OverlayManagerService extends SystemService {
}
}
- private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction)
+ @GuardedBy("mLock")
+ private void executeAllRequestsLocked(@NonNull final OverlayManagerTransaction transaction)
throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "commit " + transaction);
}
- if (transaction == null) {
- throw new IllegalArgumentException("null transaction");
- }
- synchronized (mLock) {
- // execute the requests (as calling user)
- Set<UserPackage> affectedPackagesToUpdate = null;
- for (Iterator<Request> it = transaction.getRequests(); it.hasNext(); ) {
- Request request = it.next();
- affectedPackagesToUpdate = CollectionUtils.addAll(affectedPackagesToUpdate,
- executeRequest(request));
- }
+ // execute the requests (as calling user)
+ Set<UserPackage> affectedPackagesToUpdate = null;
+ for (Iterator<Request> it = transaction.getRequests(); it.hasNext(); ) {
+ Request request = it.next();
+ affectedPackagesToUpdate = CollectionUtils.addAll(affectedPackagesToUpdate,
+ executeRequestLocked(request));
+ }
- // past the point of no return: the entire transaction has been
- // processed successfully, we can no longer fail: continue as
- // system_server
- final long ident = Binder.clearCallingIdentity();
- try {
- updateTargetPackagesLocked(affectedPackagesToUpdate);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ // past the point of no return: the entire transaction has been
+ // processed successfully, we can no longer fail: continue as
+ // system_server
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ updateTargetPackagesLocked(affectedPackagesToUpdate);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -1037,7 +1055,7 @@ public final class OverlayManagerService extends SystemService {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
final DumpState dumpState = new DumpState();
- dumpState.setUserId(UserHandle.USER_ALL);
+ int userId = UserHandle.USER_ALL;
int opti = 0;
while (opti < args.length) {
@@ -1064,9 +1082,7 @@ public final class OverlayManagerService extends SystemService {
return;
}
try {
- final int userId = UserHandle.parseUserArg(args[opti]);
- final int realUserId = handleIncomingUser(userId, "dump");
- dumpState.setUserId(realUserId);
+ userId = UserHandle.parseUserArg(args[opti]);
opti++;
} catch (Exception e) {
pw.println("Error: " + e.getMessage());
@@ -1105,6 +1121,9 @@ public final class OverlayManagerService extends SystemService {
}
enforceDumpPermission("dump");
+ final int realUserId = userId != UserHandle.USER_ALL
+ ? handleIncomingUser(userId, "dump") : userId;
+ dumpState.setUserId(realUserId);
synchronized (mLock) {
mImpl.dump(pw, dumpState);
if (dumpState.getPackageName() == null) {
@@ -1114,20 +1133,6 @@ public final class OverlayManagerService extends SystemService {
}
/**
- * Ensure that the caller has permission to interact with the given userId.
- * If the calling user is not the same as the provided user, the caller needs
- * to hold the INTERACT_ACROSS_USERS_FULL permission (or be system uid or
- * root).
- *
- * @param userId the user to interact with
- * @param message message for any SecurityException
- */
- private int handleIncomingUser(final int userId, @NonNull final String message) {
- return ActivityManager.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, true, message, null);
- }
-
- /**
* Enforce that the caller holds the DUMP permission (or is system or root).
*
* @param message used as message if SecurityException is thrown
@@ -1447,12 +1452,14 @@ public final class OverlayManagerService extends SystemService {
}
}
+ @GuardedBy("mLock")
private void updateTargetPackagesLocked(@Nullable UserPackage updatedTarget) {
if (updatedTarget != null) {
updateTargetPackagesLocked(Set.of(updatedTarget));
}
}
+ @GuardedBy("mLock")
private void updateTargetPackagesLocked(@Nullable Set<UserPackage> updatedTargets) {
if (CollectionUtils.isEmpty(updatedTargets)) {
return;
@@ -1548,6 +1555,7 @@ public final class OverlayManagerService extends SystemService {
}
@NonNull
+ @GuardedBy("mLock")
private SparseArray<List<String>> updatePackageManagerLocked(
@Nullable Set<UserPackage> targets) {
if (CollectionUtils.isEmpty(targets)) {
@@ -1568,6 +1576,7 @@ public final class OverlayManagerService extends SystemService {
* targetPackageNames: the target themselves and shared libraries)
*/
@NonNull
+ @GuardedBy("mLock")
private List<String> updatePackageManagerLocked(@NonNull Collection<String> targetPackageNames,
final int userId) {
try {
@@ -1623,6 +1632,7 @@ public final class OverlayManagerService extends SystemService {
}
}
+ @GuardedBy("mLock")
private void persistSettingsLocked() {
if (DEBUG) {
Slog.d(TAG, "Writing overlay settings");
@@ -1638,35 +1648,35 @@ public final class OverlayManagerService extends SystemService {
}
}
- private void restoreSettings() {
+ @GuardedBy("mLock")
+ private void restoreSettingsLocked() {
try {
traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
- synchronized (mLock) {
- if (!mSettingsFile.getBaseFile().exists()) {
- return;
- }
- try (FileInputStream stream = mSettingsFile.openRead()) {
- mSettings.restore(stream);
- // We might have data for dying users if the device was
- // restarted before we received USER_REMOVED. Remove data for
- // users that will not exist after the system is ready.
+ if (!mSettingsFile.getBaseFile().exists()) {
+ return;
+ }
+ try (FileInputStream stream = mSettingsFile.openRead()) {
+ mSettings.restore(stream);
- final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
- final int[] liveUserIds = new int[liveUsers.size()];
- for (int i = 0; i < liveUsers.size(); i++) {
- liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
- }
- Arrays.sort(liveUserIds);
+ // We might have data for dying users if the device was
+ // restarted before we received USER_REMOVED. Remove data for
+ // users that will not exist after the system is ready.
- for (int userId : mSettings.getUsers()) {
- if (Arrays.binarySearch(liveUserIds, userId) < 0) {
- mSettings.removeUser(userId);
- }
+ final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
+ final int[] liveUserIds = new int[liveUsers.size()];
+ for (int i = 0; i < liveUsers.size(); i++) {
+ liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
+ }
+ Arrays.sort(liveUserIds);
+
+ for (int userId : mSettings.getUsers()) {
+ if (Arrays.binarySearch(liveUserIds, userId) < 0) {
+ mSettings.removeUser(userId);
}
- } catch (IOException | XmlPullParserException e) {
- Slog.e(TAG, "failed to restore overlay state", e);
}
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "failed to restore overlay state", e);
}
} finally {
traceEnd(TRACE_TAG_RRO);
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 0e9ec4d71421..02c0190224d0 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -715,20 +715,11 @@ final class OverlayManagerServiceImpl {
}
void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
- Pair<OverlayIdentifier, String> overlayIdmap = null;
- if (dumpState.getPackageName() != null) {
- OverlayIdentifier id = new OverlayIdentifier(dumpState.getPackageName(),
- dumpState.getOverlayName());
- OverlayInfo oi = mSettings.getNullableOverlayInfo(id, USER_SYSTEM);
- if (oi != null) {
- overlayIdmap = new Pair<>(id, oi.baseCodePath);
- }
- }
-
// settings
mSettings.dump(pw, dumpState);
// idmap data
+ final var overlayIdmap = mSettings.getIdentifierAndBaseCodePath(dumpState);
if (dumpState.getField() == null) {
Set<Pair<OverlayIdentifier, String>> allIdmaps = (overlayIdmap != null)
? Set.of(overlayIdmap) : mSettings.getAllIdentifiersAndBaseCodePaths();
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index f9758fcd5d01..e6b1c5f640f2 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -212,17 +212,41 @@ final class OverlayManagerSettings {
}
Set<String> getAllBaseCodePaths() {
+ // Overlays installed for multiple users have the same code path, avoid duplicates with Set.
final Set<String> paths = new ArraySet<>();
mItems.forEach(item -> paths.add(item.mBaseCodePath));
return paths;
}
Set<Pair<OverlayIdentifier, String>> getAllIdentifiersAndBaseCodePaths() {
+ // Overlays installed for multiple users have the same code path, avoid duplicates with Set.
final Set<Pair<OverlayIdentifier, String>> set = new ArraySet<>();
- mItems.forEach(item -> set.add(new Pair(item.mOverlay, item.mBaseCodePath)));
+ mItems.forEach(item -> set.add(new Pair<>(item.mOverlay, item.mBaseCodePath)));
return set;
}
+ @Nullable
+ Pair<OverlayIdentifier, String> getIdentifierAndBaseCodePath(@NonNull DumpState dumpState) {
+ if (dumpState.getPackageName() == null) {
+ return null;
+ }
+ OverlayIdentifier id = new OverlayIdentifier(dumpState.getPackageName(),
+ dumpState.getOverlayName());
+ final int userId = dumpState.getUserId();
+ for (int i = 0; i < mItems.size(); i++) {
+ final var item = mItems.get(i);
+ if (userId != UserHandle.USER_ALL && userId != item.mUserId) {
+ continue;
+ }
+ if (!id.equals(item.mOverlay)) {
+ continue;
+ }
+ // Overlays installed for multiple users have the same code path, return first found.
+ return new Pair<>(id, item.mBaseCodePath);
+ }
+ return null;
+ }
+
@NonNull
List<OverlayInfo> removeIf(@NonNull final Predicate<OverlayInfo> predicate, final int userId) {
return removeIf(info -> (predicate.test(info) && info.userId == userId));
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index b7f21125148c..39a6b1895c3a 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.om;
import static com.android.internal.content.om.OverlayConfig.PARTITION_ORDER_FILE_PATH;
+import static com.android.server.om.OverlayManagerService.handleIncomingUser;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -145,7 +146,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
out.println(" Load a package and print the value of a given resource");
out.println(" applying the current configuration and enabled overlays.");
out.println(" For a more fine-grained alternative, use 'idmap2 lookup'.");
- out.println(" fabricate [--user USER_ID] [--target-name OVERLAYABLE] --target PACKAGE");
+ out.println(" fabricate [--target-name OVERLAYABLE] --target PACKAGE");
out.println(" --name NAME [--file FILE] ");
out.println(" PACKAGE:TYPE/NAME ENCODED-TYPE-ID|TYPE-NAME ENCODED-VALUE");
out.println(" Create an overlay from a single resource. Caller must be root. Example:");
@@ -160,7 +161,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
final PrintWriter out = getOutPrintWriter();
final PrintWriter err = getErrPrintWriter();
- int userId = UserHandle.USER_SYSTEM;
+ int userId = UserHandle.USER_CURRENT;
String opt;
while ((opt = getNextOption()) != null) {
switch (opt) {
@@ -234,7 +235,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
private int runEnableDisable(final boolean enable) throws RemoteException {
final PrintWriter err = getErrPrintWriter();
- int userId = UserHandle.USER_SYSTEM;
+ int userId = UserHandle.USER_CURRENT;
String opt;
while ((opt = getNextOption()) != null) {
switch (opt) {
@@ -269,7 +270,6 @@ final class OverlayManagerShellCommand extends ShellCommand {
return 1;
}
- int userId = UserHandle.USER_SYSTEM;
String targetPackage = "";
String targetOverlayable = "";
String name = "";
@@ -278,9 +278,6 @@ final class OverlayManagerShellCommand extends ShellCommand {
String config = null;
while ((opt = getNextOption()) != null) {
switch (opt) {
- case "--user":
- userId = UserHandle.parseUserArg(getNextArgRequired());
- break;
case "--target":
targetPackage = getNextArgRequired();
break;
@@ -442,7 +439,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
private int runEnableExclusive() throws RemoteException {
final PrintWriter err = getErrPrintWriter();
- int userId = UserHandle.USER_SYSTEM;
+ int userId = UserHandle.USER_CURRENT;
boolean inCategory = false;
String opt;
while ((opt = getNextOption()) != null) {
@@ -469,7 +466,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
private int runSetPriority() throws RemoteException {
final PrintWriter err = getErrPrintWriter();
- int userId = UserHandle.USER_SYSTEM;
+ int userId = UserHandle.USER_CURRENT;
String opt;
while ((opt = getNextOption()) != null) {
switch (opt) {
@@ -498,7 +495,7 @@ final class OverlayManagerShellCommand extends ShellCommand {
final PrintWriter out = getOutPrintWriter();
final PrintWriter err = getErrPrintWriter();
- int userId = UserHandle.USER_SYSTEM;
+ int userId = UserHandle.USER_CURRENT;
boolean verbose = false;
String opt;
while ((opt = getNextOption()) != null) {
@@ -525,15 +522,16 @@ final class OverlayManagerShellCommand extends ShellCommand {
return 1;
}
+ final int realUserId = handleIncomingUser(userId, "runLookup");
final Resources res;
try {
res = mContext
- .createContextAsUser(UserHandle.of(userId), /* flags */ 0)
+ .createContextAsUser(UserHandle.of(realUserId), /* flags */ 0)
.getPackageManager()
.getResourcesForApplication(packageToLoad);
} catch (PackageManager.NameNotFoundException e) {
err.println(String.format("Error: failed to get resources for package %s for user %d",
- packageToLoad, userId));
+ packageToLoad, realUserId));
return 1;
}
final AssetManager assets = res.getAssets();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 09b10739d469..d9c79b5c40bb 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -781,13 +781,71 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (mLastWallpaper == null || mFallbackWallpaper == null) return;
final WallpaperConnection systemConnection = mLastWallpaper.connection;
final WallpaperConnection fallbackConnection = mFallbackWallpaper.connection;
+ final WallpaperConnection lockConnection;
+ if (mLastLockWallpaper != null) {
+ lockConnection = mLastLockWallpaper.connection;
+ } else {
+ lockConnection = null;
+ }
if (fallbackConnection == null) {
Slog.w(TAG, "Fallback wallpaper connection has not been created yet!!");
return;
}
- // TODO(b/384520326) Passing DEFAULT_DISPLAY temporarily before we revamp the
- // multi-display supports.
- if (isWallpaperCompatibleForDisplay(DEFAULT_DISPLAY, systemConnection)) {
+
+ if (enableConnectedDisplaysWallpaper()) {
+ mWallpaperDisplayHelper.forEachDisplayData(displayData -> {
+ int displayId = displayData.mDisplayId;
+ // If the display is already connected to the desired wallpaper(s), either the
+ // same wallpaper for both lock and system, or different wallpapers for each,
+ // any existing fallback wallpaper connection will be removed.
+ if (systemConnection.containsDisplay(displayId)
+ && (lockConnection == null || lockConnection.containsDisplay(displayId))) {
+ DisplayConnector fallbackConnector =
+ mFallbackWallpaper.connection.mDisplayConnector.get(displayId);
+ if (fallbackConnector != null && fallbackConnector.mEngine != null) {
+ fallbackConnector.disconnectLocked(mFallbackWallpaper.connection);
+ mFallbackWallpaper.connection.mDisplayConnector.remove(displayId);
+ }
+ return;
+ }
+
+ // Identify if the fallback wallpaper should be use for lock or system or both.
+ int which = 0;
+ if (!systemConnection.containsDisplay(displayId)) {
+ which |= FLAG_SYSTEM;
+ }
+ if (lockConnection == null || !lockConnection.containsDisplay(displayId)) {
+ which |= FLAG_LOCK;
+ }
+ if (mFallbackWallpaper.connection.containsDisplay(displayId)) {
+ // For existing fallback wallpaper connection, update the `which` flags.
+ DisplayConnector fallbackConnector =
+ mFallbackWallpaper.connection.mDisplayConnector.get(displayId);
+ try {
+ if (fallbackConnector != null && fallbackConnector.mEngine != null
+ && fallbackConnector.mWhich != which) {
+ fallbackConnector.mEngine.setWallpaperFlags(which);
+ mWindowManagerInternal.setWallpaperShowWhenLocked(
+ fallbackConnector.mToken,
+ /* showWhenLocked= */ (which & FLAG_LOCK) != 0);
+ fallbackConnector.mWhich = which;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to update fallback wallpaper engine flags", e);
+ }
+ } else {
+ // For new fallback connection, establish the connection with the desired
+ // `which` flag.
+ DisplayConnector fallbackConnector =
+ mFallbackWallpaper.connection.getDisplayConnectorOrCreate(displayId);
+ if (fallbackConnector != null && fallbackConnector.mEngine != null) {
+ fallbackConnector.mWhich = which;
+ fallbackConnector.connectLocked(mFallbackWallpaper.connection,
+ mFallbackWallpaper);
+ }
+ }
+ });
+ } else if (isWallpaperCompatibleForDisplay(DEFAULT_DISPLAY, systemConnection)) {
if (fallbackConnection.mDisplayConnector.size() != 0) {
fallbackConnection.forEachDisplayConnector(connector -> {
if (connector.mEngine != null) {
@@ -820,8 +878,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
boolean mDimensionsChanged;
boolean mPaddingChanged;
- DisplayConnector(int displayId) {
+ // This field is added for the fallback wallpaper, which may have a different which flag for
+ // a different display.
+ int mWhich;
+
+ DisplayConnector(int displayId, int which) {
mDisplayId = displayId;
+ mWhich = which;
}
void ensureStatusHandled() {
@@ -850,13 +913,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.w(TAG, "WallpaperService is not connected yet");
return;
}
+ int which = wallpaper.mWhich;
+ if (enableConnectedDisplaysWallpaper()) {
+ which = mWhich;
+ }
TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
t.traceBegin("WPMS.connectLocked-" + wallpaper.getComponent());
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
null /* options */);
mWindowManagerInternal.setWallpaperShowWhenLocked(
- mToken, (wallpaper.mWhich & FLAG_LOCK) != 0);
+ mToken, (which & FLAG_LOCK) != 0);
if (multiCrop() && mImageWallpaper.equals(wallpaper.getComponent())) {
mWindowManagerInternal.setWallpaperCropHints(mToken,
mWallpaperCropper.getRelativeCropHints(wallpaper));
@@ -868,16 +935,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
try {
if (liveWallpaperContentHandling()) {
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
- wpdData.mWidth, wpdData.mHeight,
- wpdData.mPadding, mDisplayId, wallpaper.mWhich, connection.mInfo,
- wallpaper.getDescription());
+ wpdData.mWidth, wpdData.mHeight, wpdData.mPadding, mDisplayId, which,
+ connection.mInfo, wallpaper.getDescription());
} else {
WallpaperDescription desc = new WallpaperDescription.Builder().setComponent(
(connection.mInfo != null) ? connection.mInfo.getComponent()
: null).build();
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
- wpdData.mWidth, wpdData.mHeight,
- wpdData.mPadding, mDisplayId, wallpaper.mWhich, connection.mInfo, desc);
+ wpdData.mWidth, wpdData.mHeight, wpdData.mPadding, mDisplayId, which,
+ connection.mInfo, desc);
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper on display", e);
@@ -980,7 +1046,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
final int displayId = display.getDisplayId();
final DisplayConnector connector = mDisplayConnector.get(displayId);
if (connector == null) {
- mDisplayConnector.append(displayId, new DisplayConnector(displayId));
+ mDisplayConnector.append(displayId,
+ new DisplayConnector(displayId, mWallpaper.mWhich));
}
}
}
@@ -1006,7 +1073,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
DisplayConnector connector = mDisplayConnector.get(displayId);
if (connector == null) {
if (mWallpaperDisplayHelper.isUsableDisplay(displayId, mClientUid)) {
- connector = new DisplayConnector(displayId);
+ connector = new DisplayConnector(displayId, mWallpaper.mWhich);
mDisplayConnector.append(displayId, connector);
}
}
@@ -1364,6 +1431,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
Slog.v(TAG, "static system+lock to system failure");
}
WallpaperData currentSystem = mWallpaperMap.get(mNewWallpaper.userId);
+ // In the constructor, we copied the system+lock wallpaper to
+ // mOriginalSystem. However, the copied WallpaperData#connection is a
+ // reference, not a deep copy. This means
+ // currentSystem.connection.mWallpaper points to mOriginalSystem, so
+ // changes to currentSystem.mWhich alone won't update the corresponding
+ // flag in currentSystem.connection.mWallpaper.mWhich. Let's point
+ // currentSystem.connection.mWallpaper back to currentSystem.
+ if (enableConnectedDisplaysWallpaper()
+ && currentSystem.connection != null) {
+ currentSystem.connection.mWallpaper = currentSystem;
+ }
currentSystem.mWhich = FLAG_SYSTEM | FLAG_LOCK;
updateEngineFlags(currentSystem);
mLockWallpaperMap.remove(mNewWallpaper.userId);
@@ -1385,6 +1463,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
WallpaperData currentSystem = mWallpaperMap.get(mNewWallpaper.userId);
if (currentSystem.wallpaperId == mOriginalSystem.wallpaperId) {
+ // Fixing the reference, see above for more details.
+ if (enableConnectedDisplaysWallpaper()
+ && currentSystem.connection != null) {
+ currentSystem.connection.mWallpaper = currentSystem;
+ }
currentSystem.mWhich = FLAG_SYSTEM;
updateEngineFlags(currentSystem);
}
@@ -3814,6 +3897,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
wallpaper.connection.forEachDisplayConnector(
connector -> {
try {
+ connector.mWhich = wallpaper.mWhich;
if (connector.mEngine != null) {
connector.mEngine.setWallpaperFlags(wallpaper.mWhich);
mWindowManagerInternal.setWallpaperShowWhenLocked(
@@ -3949,8 +4033,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (mLastWallpaper == null) {
return;
}
+ int useFallbackWallpaperWhich = 0;
if (enableConnectedDisplaysWallpaper()) {
- int useFallbackWallpaperWhich = 0;
List<WallpaperData> wallpapers = new ArrayList<>();
wallpapers.add(mLastWallpaper);
// If the system and the lock wallpapers are not the same, we should also
@@ -3981,7 +4065,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
|| mFallbackWallpaper.connection == null) {
return;
}
- mFallbackWallpaper.mWhich = useFallbackWallpaperWhich;
} else {
if (isWallpaperCompatibleForDisplay(displayId, mLastWallpaper.connection)) {
final DisplayConnector connector =
@@ -3999,6 +4082,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
final DisplayConnector connector = mFallbackWallpaper
.connection.getDisplayConnectorOrCreate(displayId);
if (connector == null) return;
+ connector.mWhich = useFallbackWallpaperWhich;
connector.connectLocked(mFallbackWallpaper.connection, mFallbackWallpaper);
} else {
Slog.w(TAG, "No wallpaper can be added to the new display");
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 89b46bc4eba4..e8498bca1809 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -178,6 +178,7 @@ import static com.android.server.wm.ActivityRecordProto.PROC_ID;
import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS;
import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
+import static com.android.server.wm.ActivityRecordProto.REQUEST_OPEN_IN_BROWSER_EDUCATION_TIMESTAMP;
import static com.android.server.wm.ActivityRecordProto.SHOULD_ENABLE_USER_ASPECT_RATIO_SETTINGS;
import static com.android.server.wm.ActivityRecordProto.SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT;
import static com.android.server.wm.ActivityRecordProto.SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP;
@@ -9961,6 +9962,8 @@ final class ActivityRecord extends WindowToken {
aspectRatioOverrides.shouldEnableUserAspectRatioSettings());
proto.write(IS_USER_FULLSCREEN_OVERRIDE_ENABLED,
aspectRatioOverrides.isUserFullscreenOverrideEnabled());
+ proto.write(REQUEST_OPEN_IN_BROWSER_EDUCATION_TIMESTAMP,
+ mRequestOpenInBrowserEducationTimestamp);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 906befc1edcc..0d88a9b1f8ee 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -145,6 +145,7 @@ import android.util.SparseIntArray;
import android.view.Display;
import android.webkit.URLUtil;
import android.window.ActivityWindowInfo;
+import android.window.DesktopModeFlags;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -161,7 +162,6 @@ import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.pm.SaferIntentUtils;
import com.android.server.utils.Slogf;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
-import com.android.window.flags.Flags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -2974,7 +2974,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
@Override
public void accept(ActivityRecord r) {
- if (Flags.enableDesktopWindowingAppToWeb() && mInfo.capturedLink == null) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_APP_TO_WEB.isTrue()
+ && mInfo.capturedLink == null) {
setCapturedLink(r);
}
if (r.mLaunchCookie != null) {
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 59ca79c7ffc3..cf16204f93a1 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -182,12 +182,18 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
if (control != null && control.getLeash() != null) {
ImeTracker.Token statsToken = getAndClearStatsToken();
if (statsToken == null) {
- ProtoLog.d(WM_DEBUG_IME, "IME getControl without statsToken");
- } else {
- ImeTracker.forLogging().onProgress(statsToken,
- ImeTracker.PHASE_WM_GET_CONTROL_WITH_LEASH);
- control.setImeStatsToken(statsToken);
+ ProtoLog.w(WM_DEBUG_IME,
+ "IME getControl without statsToken (check previous request!). "
+ + "Start new request");
+ // TODO(b/353463205) remove this later after fixing the race of two requests
+ // that cancel each other (cf. b/383466954#comment19).
+ statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
+ ImeTracker.ORIGIN_SERVER, SoftInputShowHideReason.CONTROLS_CHANGED,
+ false /* fromUser */);
}
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_WM_GET_CONTROL_WITH_LEASH);
+ control.setImeStatsToken(statsToken);
}
}
return control;
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 d80fd20dd1e6..ef77a0ee067f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -1028,6 +1028,154 @@ public class WallpaperManagerServiceTests {
}
// Verify a secondary display removes system decorations ended
+ // Test setWallpaperComponent on multiple displays.
+ // GIVEN 3 displays, 0, 2, 3, the new wallpaper is only compatible for display 0 and 3 but not
+ // 2.
+ // WHEN the new wallpaper is set for system and lock via setWallpaperComponent.
+ // THEN there are 2 connections in mLastWallpaper and 1 connection in mFallbackWallpaper.
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ public void setWallpaperComponent_multiDisplays_shouldHaveExpectedConnections() {
+ // Skip if there is no pre-defined default wallpaper component.
+ assumeThat(sDefaultWallpaperComponent,
+ not(CoreMatchers.equalTo(sImageWallpaperComponentName)));
+
+ final int testUserId = USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ final int incompatibleDisplayId = 2;
+ final int compatibleDisplayId = 3;
+ setUpDisplays(List.of(DEFAULT_DISPLAY, incompatibleDisplayId, compatibleDisplayId));
+ mService.removeWallpaperCompatibleDisplayForTest(incompatibleDisplayId);
+
+ mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
+ FLAG_SYSTEM | FLAG_LOCK, testUserId);
+
+ verifyLastWallpaperData(testUserId, sImageWallpaperComponentName);
+ verifyCurrentSystemData(testUserId);
+ assertThat(mService.mLastWallpaper.connection.getConnectedEngineSize()).isEqualTo(2);
+ assertThat(mService.mLastWallpaper.connection.containsDisplay(DEFAULT_DISPLAY)).isTrue();
+ assertThat(mService.mLastWallpaper.connection.containsDisplay(compatibleDisplayId))
+ .isTrue();
+ assertThat(mService.mFallbackWallpaper.connection.getConnectedEngineSize()).isEqualTo(1);
+ assertThat(mService.mFallbackWallpaper.connection.containsDisplay(incompatibleDisplayId))
+ .isTrue();
+ assertThat(mService.mLastLockWallpaper).isNull();
+ }
+
+ // Test setWallpaperComponent on multiple displays.
+ // GIVEN 3 displays, 0, 2, 3, the existing wallpaper is only compatible for display 0 and 3 but
+ // not 2.
+ // GIVEN the new wallpaper is compatible to 2.
+ // WHEN the new wallpaper is set for system and lock via setWallpaperComponent.
+ // THEN there are 3 connections in mLastWallpaper and 0 connection in mFallbackWallpaper.
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ public void setWallpaperComponent_multiDisplays_displayBecomeCompatible_shouldHaveExpectedConnections() {
+ // Skip if there is no pre-defined default wallpaper component.
+ assumeThat(sDefaultWallpaperComponent,
+ not(CoreMatchers.equalTo(sImageWallpaperComponentName)));
+
+ final int testUserId = USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ final int display2 = 2;
+ final int display3 = 3;
+ setUpDisplays(List.of(DEFAULT_DISPLAY, display2, display3));
+ mService.removeWallpaperCompatibleDisplayForTest(display2);
+ mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
+ FLAG_SYSTEM | FLAG_LOCK, testUserId);
+
+ mService.addWallpaperCompatibleDisplayForTest(display2);
+ mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
+ FLAG_SYSTEM | FLAG_LOCK, testUserId);
+
+ verifyLastWallpaperData(testUserId, sImageWallpaperComponentName);
+ verifyCurrentSystemData(testUserId);
+ assertThat(mService.mLastWallpaper.connection.getConnectedEngineSize()).isEqualTo(3);
+ assertThat(mService.mLastWallpaper.connection.containsDisplay(DEFAULT_DISPLAY)).isTrue();
+ assertThat(mService.mLastWallpaper.connection.containsDisplay(display2)).isTrue();
+ assertThat(mService.mLastWallpaper.connection.containsDisplay(display3)).isTrue();
+ assertThat(mService.mFallbackWallpaper.connection.getConnectedEngineSize()).isEqualTo(0);
+ assertThat(mService.mLastLockWallpaper).isNull();
+ }
+
+ // Test setWallpaperComponent on multiple displays.
+ // GIVEN 3 displays, 0, 2, 3, the existing wallpaper is only compatible for display 0 and 3 but
+ // not 2.
+ // GIVEN the new wallpaper is incompatible to 2 and 3.
+ // WHEN the new wallpaper is set for system and lock via setWallpaperComponent.
+ // THEN there are 1 connections in mLastWallpaper and 2 connection in mFallbackWallpaper.
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ public void setWallpaperComponent_multiDisplays_displayBecomeIncompatible_shouldHaveExpectedConnections() {
+ // Skip if there is no pre-defined default wallpaper component.
+ assumeThat(sDefaultWallpaperComponent,
+ not(CoreMatchers.equalTo(sImageWallpaperComponentName)));
+
+ final int testUserId = USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ final int display2 = 2;
+ final int display3 = 3;
+ setUpDisplays(List.of(DEFAULT_DISPLAY, display2, display3));
+ mService.removeWallpaperCompatibleDisplayForTest(display2);
+ mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
+ FLAG_SYSTEM | FLAG_LOCK, testUserId);
+
+ mService.removeWallpaperCompatibleDisplayForTest(display3);
+ mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
+ FLAG_SYSTEM | FLAG_LOCK, testUserId);
+
+ verifyLastWallpaperData(testUserId, sImageWallpaperComponentName);
+ verifyCurrentSystemData(testUserId);
+ assertThat(mService.mLastWallpaper.connection.getConnectedEngineSize()).isEqualTo(1);
+ assertThat(mService.mLastWallpaper.connection.containsDisplay(DEFAULT_DISPLAY)).isTrue();
+ assertThat(mService.mFallbackWallpaper.connection.getConnectedEngineSize()).isEqualTo(2);
+ assertThat(mService.mFallbackWallpaper.connection.containsDisplay(display2)).isTrue();
+ assertThat(mService.mFallbackWallpaper.connection.containsDisplay(display3)).isTrue();
+ assertThat(mService.mLastLockWallpaper).isNull();
+ }
+
+ // Test setWallpaperComponent on multiple displays.
+ // GIVEN 3 displays, 0, 2, 3, the new wallpaper is only compatible for display 0 and 3 but not
+ // 2.
+ // WHEN two different wallpapers set for system and lock via setWallpaperComponent.
+ // THEN there are two connections in mLastWallpaper, two connection in mLastLockWallpaper and
+ // one connection in mFallbackWallpaper.
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WALLPAPER)
+ public void setWallpaperComponent_systemAndLockWallpapers_multiDisplays_shouldHaveExpectedConnections() {
+ // Skip if there is no pre-defined default wallpaper component.
+ assumeThat(sDefaultWallpaperComponent,
+ not(CoreMatchers.equalTo(sImageWallpaperComponentName)));
+
+ final int testUserId = USER_SYSTEM;
+ mService.switchUser(testUserId, null);
+ final int incompatibleDisplayId = 2;
+ final int compatibleDisplayId = 3;
+ setUpDisplays(List.of(DEFAULT_DISPLAY, incompatibleDisplayId, compatibleDisplayId));
+ mService.removeWallpaperCompatibleDisplayForTest(incompatibleDisplayId);
+
+ mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
+ FLAG_SYSTEM, testUserId);
+ mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
+ FLAG_LOCK, testUserId);
+
+ verifyLastWallpaperData(testUserId, sImageWallpaperComponentName);
+ verifyLastLockWallpaperData(testUserId, sImageWallpaperComponentName);
+ verifyCurrentSystemData(testUserId);
+ assertThat(mService.mLastWallpaper.connection.getConnectedEngineSize()).isEqualTo(2);
+ assertThat(mService.mLastWallpaper.connection.containsDisplay(DEFAULT_DISPLAY)).isTrue();
+ assertThat(mService.mLastWallpaper.connection.containsDisplay(compatibleDisplayId))
+ .isTrue();
+ assertThat(mService.mLastLockWallpaper.connection.getConnectedEngineSize()).isEqualTo(2);
+ assertThat(mService.mLastLockWallpaper.connection.containsDisplay(DEFAULT_DISPLAY))
+ .isTrue();
+ assertThat(mService.mLastLockWallpaper.connection.containsDisplay(compatibleDisplayId))
+ .isTrue();
+ assertThat(mService.mFallbackWallpaper.connection.getConnectedEngineSize()).isEqualTo(1);
+ assertThat(mService.mFallbackWallpaper.connection.containsDisplay(incompatibleDisplayId))
+ .isTrue();
+ }
+
// 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/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index f02bdae1d9e6..457fde8d74d0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -20,11 +20,11 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.server.testutils.MockitoUtilsKt.eq;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertNotNull;
-import static org.testng.AssertJUnit.assertNull;
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
@@ -35,6 +35,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.view.InputDevice;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -82,69 +83,89 @@ public class AutoclickControllerTest {
@Test
public void onMotionEvent_lazyInitClickScheduler() {
- assertNull(mController.mClickScheduler);
+ assertThat(mController.mClickScheduler).isNull();
injectFakeMouseActionDownEvent();
- assertNotNull(mController.mClickScheduler);
+ assertThat(mController.mClickScheduler).isNotNull();
}
@Test
public void onMotionEvent_nonMouseSource_notInitClickScheduler() {
- assertNull(mController.mClickScheduler);
+ assertThat(mController.mClickScheduler).isNull();
injectFakeNonMouseActionDownEvent();
- assertNull(mController.mClickScheduler);
+ assertThat(mController.mClickScheduler).isNull();
}
@Test
public void onMotionEvent_lazyInitAutoclickSettingsObserver() {
- assertNull(mController.mAutoclickSettingsObserver);
+ assertThat(mController.mAutoclickSettingsObserver).isNull();
injectFakeMouseActionDownEvent();
- assertNotNull(mController.mAutoclickSettingsObserver);
+ assertThat(mController.mAutoclickSettingsObserver).isNotNull();
}
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void onMotionEvent_flagOn_lazyInitAutoclickIndicatorScheduler() {
- assertNull(mController.mAutoclickIndicatorScheduler);
+ assertThat(mController.mAutoclickIndicatorScheduler).isNull();
injectFakeMouseActionDownEvent();
- assertNotNull(mController.mAutoclickIndicatorScheduler);
+ assertThat(mController.mAutoclickIndicatorScheduler).isNotNull();
}
@Test
@DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void onMotionEvent_flagOff_notInitAutoclickIndicatorScheduler() {
- assertNull(mController.mAutoclickIndicatorScheduler);
+ assertThat(mController.mAutoclickIndicatorScheduler).isNull();
injectFakeMouseActionDownEvent();
- assertNull(mController.mAutoclickIndicatorScheduler);
+ assertThat(mController.mAutoclickIndicatorScheduler).isNull();
}
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void onMotionEvent_flagOn_lazyInitAutoclickIndicatorView() {
- assertNull(mController.mAutoclickIndicatorView);
+ assertThat(mController.mAutoclickIndicatorView).isNull();
injectFakeMouseActionDownEvent();
- assertNotNull(mController.mAutoclickIndicatorView);
+ assertThat(mController.mAutoclickIndicatorView).isNotNull();
}
@Test
@DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void onMotionEvent_flagOff_notInitAutoclickIndicatorView() {
- assertNull(mController.mAutoclickIndicatorView);
+ assertThat(mController.mAutoclickIndicatorView).isNull();
+
+ injectFakeMouseActionDownEvent();
+
+ assertThat(mController.mAutoclickIndicatorView).isNull();
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOn_lazyInitAutoclickTypePanelView() {
+ assertThat(mController.mAutoclickTypePanel).isNull();
+
+ injectFakeMouseActionDownEvent();
+
+ assertThat(mController.mAutoclickTypePanel).isNotNull();
+ }
+
+ @Test
+ @DisableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onMotionEvent_flagOff_notInitAutoclickTypePanelView() {
+ assertThat(mController.mAutoclickTypePanel).isNull();
injectFakeMouseActionDownEvent();
- assertNull(mController.mAutoclickIndicatorView);
+ assertThat(mController.mAutoclickTypePanel).isNull();
}
@Test
@@ -166,6 +187,18 @@ public class AutoclickControllerTest {
}
@Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onDestroy_flagOn_removeAutoclickTypePanelViewToWindowManager() {
+ injectFakeMouseActionDownEvent();
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ mController.onDestroy();
+
+ verify(mockAutoclickTypePanel).hide();
+ }
+
+ @Test
public void onMotionEvent_initClickSchedulerDelayFromSetting() {
injectFakeMouseActionDownEvent();
@@ -175,7 +208,7 @@ public class AutoclickControllerTest {
Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
AccessibilityManager.AUTOCLICK_DELAY_DEFAULT,
mTestableContext.getUserId());
- assertEquals(delay, mController.mClickScheduler.getDelayForTesting());
+ assertThat(mController.mClickScheduler.getDelayForTesting()).isEqualTo(delay);
}
@Test
@@ -189,7 +222,40 @@ public class AutoclickControllerTest {
Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT,
mTestableContext.getUserId());
- assertEquals(size, mController.mAutoclickIndicatorView.getRadiusForTesting());
+ assertThat(mController.mAutoclickIndicatorView.getRadiusForTesting()).isEqualTo(size);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onKeyEvent_modifierKey_doNotUpdateMetaStateWhenControllerIsNull() {
+ assertThat(mController.mClickScheduler).isNull();
+
+ injectFakeKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.META_ALT_ON);
+
+ assertThat(mController.mClickScheduler).isNull();
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onKeyEvent_modifierKey_updateMetaStateWhenControllerNotNull() {
+ injectFakeMouseActionDownEvent();
+
+ int metaState = KeyEvent.META_ALT_ON | KeyEvent.META_META_ON;
+ injectFakeKeyEvent(KeyEvent.KEYCODE_ALT_LEFT, metaState);
+
+ assertThat(mController.mClickScheduler).isNotNull();
+ assertThat(mController.mClickScheduler.getMetaStateForTesting()).isEqualTo(metaState);
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void onKeyEvent_modifierKey_cancelAutoClickWhenAdditionalRegularKeyPresssed() {
+ injectFakeMouseActionDownEvent();
+
+ injectFakeKeyEvent(KeyEvent.KEYCODE_J, KeyEvent.META_ALT_ON);
+
+ assertThat(mController.mClickScheduler).isNotNull();
+ assertThat(mController.mClickScheduler.getMetaStateForTesting()).isEqualTo(0);
}
@Test
@@ -198,7 +264,7 @@ public class AutoclickControllerTest {
mController.onDestroy();
- assertNull(mController.mClickScheduler);
+ assertThat(mController.mClickScheduler).isNull();
}
@Test
@@ -207,7 +273,7 @@ public class AutoclickControllerTest {
mController.onDestroy();
- assertNull(mController.mAutoclickSettingsObserver);
+ assertThat(mController.mAutoclickSettingsObserver).isNull();
}
@Test
@@ -217,7 +283,7 @@ public class AutoclickControllerTest {
mController.onDestroy();
- assertNull(mController.mAutoclickIndicatorScheduler);
+ assertThat(mController.mAutoclickIndicatorScheduler).isNull();
}
private void injectFakeMouseActionDownEvent() {
@@ -232,6 +298,17 @@ public class AutoclickControllerTest {
mController.onMotionEvent(event, event, /* policyFlags= */ 0);
}
+ private void injectFakeKeyEvent(int keyCode, int modifiers) {
+ KeyEvent keyEvent = new KeyEvent(
+ /* downTime= */ 0,
+ /* eventTime= */ 0,
+ /* action= */ KeyEvent.ACTION_DOWN,
+ /* code= */ keyCode,
+ /* repeat= */ 0,
+ /* metaState= */ modifiers);
+ mController.onKeyEvent(keyEvent, /* policyFlags= */ 0);
+ }
+
private MotionEvent getFakeMotionDownEvent() {
return MotionEvent.obtain(
/* downTime= */ 0,
diff --git a/services/tests/servicestests/src/com/android/server/zram/ZramMaintenanceTest.kt b/services/tests/servicestests/src/com/android/server/zram/ZramMaintenanceTest.kt
new file mode 100644
index 000000000000..5448a05aafc3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/zram/ZramMaintenanceTest.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2024 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.server.zram
+
+import android.app.job.JobInfo
+import android.app.job.JobParameters
+import android.app.job.JobScheduler
+import android.os.IMmd
+import android.os.PersistableBundle
+import android.os.RemoteException
+import android.testing.TestableContext
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+
+import com.android.server.ZramMaintenance
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+private fun generateJobParameters(jobId: Int, extras: PersistableBundle): JobParameters {
+ return JobParameters(
+ null, "", jobId, extras, null, null, 0, false, false, false, null, null, null
+ )
+}
+
+@SmallTest
+@RunWith(JUnit4::class)
+class ZramMaintenanceTest {
+ private val context = TestableContext(InstrumentationRegistry.getInstrumentation().context)
+
+ @Captor
+ private lateinit var jobInfoCaptor: ArgumentCaptor<JobInfo>
+
+ @Mock
+ private lateinit var mockJobScheduler: JobScheduler
+
+ @Mock
+ private lateinit var mockMmd: IMmd
+
+ @Before
+ @Throws(RemoteException::class)
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ context.addMockSystemService(JobScheduler::class.java, mockJobScheduler)
+ }
+
+ @Test
+ fun startZramMaintenance() {
+ ZramMaintenance.startZramMaintenance(context)
+
+ verify(mockJobScheduler, times(1)).schedule(jobInfoCaptor.capture())
+ val job = jobInfoCaptor.value
+ assertThat(job.id).isEqualTo(ZramMaintenance.JOB_ID)
+ assertThat(job.extras.getBoolean(ZramMaintenance.KEY_CHECK_STATUS)).isTrue()
+ }
+
+ @Test
+ fun startJobForFirstTime() {
+ val extras = PersistableBundle()
+ extras.putBoolean(ZramMaintenance.KEY_CHECK_STATUS, true)
+ val params = generateJobParameters(
+ ZramMaintenance.JOB_ID,
+ extras,
+ )
+ `when`(mockMmd.isZramMaintenanceSupported()).thenReturn(true)
+
+ ZramMaintenance.startJob(context, params, mockMmd)
+
+ verify(mockMmd, times(1)).isZramMaintenanceSupported()
+ verify(mockMmd, times(1)).doZramMaintenanceAsync()
+ verify(mockJobScheduler, times(1)).schedule(jobInfoCaptor.capture())
+ val nextJob = jobInfoCaptor.value
+ assertThat(nextJob.id).isEqualTo(ZramMaintenance.JOB_ID)
+ assertThat(nextJob.extras.getBoolean(ZramMaintenance.KEY_CHECK_STATUS)).isFalse()
+ }
+
+ @Test
+ fun startJobWithoutCheckStatus() {
+ val extras = PersistableBundle()
+ extras.putBoolean(ZramMaintenance.KEY_CHECK_STATUS, false)
+ val params = generateJobParameters(
+ ZramMaintenance.JOB_ID,
+ extras,
+ )
+
+ ZramMaintenance.startJob(context, params, mockMmd)
+
+ verify(mockMmd, never()).isZramMaintenanceSupported()
+ verify(mockMmd, times(1)).doZramMaintenanceAsync()
+ verify(mockJobScheduler, times(1)).schedule(jobInfoCaptor.capture())
+ val nextJob = jobInfoCaptor.value
+ assertThat(nextJob.id).isEqualTo(ZramMaintenance.JOB_ID)
+ assertThat(nextJob.extras.getBoolean(ZramMaintenance.KEY_CHECK_STATUS)).isFalse()
+ }
+
+ @Test
+ fun startJobZramIsDisabled() {
+ val extras = PersistableBundle()
+ extras.putBoolean(ZramMaintenance.KEY_CHECK_STATUS, true)
+ val params = generateJobParameters(
+ ZramMaintenance.JOB_ID,
+ extras,
+ )
+ `when`(mockMmd.isZramMaintenanceSupported()).thenReturn(false)
+
+ ZramMaintenance.startJob(context, params, mockMmd)
+
+ verify(mockMmd, times(1)).isZramMaintenanceSupported()
+ verify(mockMmd, never()).doZramMaintenanceAsync()
+ verify(mockJobScheduler, never()).schedule(any())
+ }
+
+ @Test
+ fun startJobMmdIsNotReadyYet() {
+ val extras = PersistableBundle()
+ extras.putBoolean(ZramMaintenance.KEY_CHECK_STATUS, true)
+ val params = generateJobParameters(
+ ZramMaintenance.JOB_ID,
+ extras,
+ )
+
+ ZramMaintenance.startJob(context, params, null)
+
+ verify(mockJobScheduler, times(1)).schedule(jobInfoCaptor.capture())
+ val nextJob = jobInfoCaptor.value
+ assertThat(nextJob.id).isEqualTo(ZramMaintenance.JOB_ID)
+ assertThat(nextJob.extras.getBoolean(ZramMaintenance.KEY_CHECK_STATUS)).isTrue()
+ }
+} \ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 31f9a1cffe7c..a9e8f4a0d965 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -260,6 +260,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public static List<FlagsParameterization> getParams() {
return FlagsParameterization.allCombinationsOf(
android.app.Flags.FLAG_API_RICH_ONGOING,
+ android.app.Flags.FLAG_UI_RICH_ONGOING,
FLAG_NOTIFICATION_CLASSIFICATION, FLAG_NOTIFICATION_CLASSIFICATION_UI,
FLAG_MODES_UI, android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS);
}
@@ -662,6 +663,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
if (android.app.Flags.uiRichOngoing()) {
+ mHelper.setCanBePromoted(PKG_N_MR1, UID_N_MR1, false, true);
mHelper.setCanBePromoted(PKG_N_MR1, UID_N_MR1, true, true);
}
@@ -6643,6 +6645,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING)
public void testSetCanBePromoted_allowlistNotOverrideUser() {
+ // default value is true. So we need to set it false to trigger the change.
+ mHelper.setCanBePromoted(PKG_P, UID_P, false, true);
mHelper.setCanBePromoted(PKG_P, UID_P, true, true);
assertThat(mHelper.canBePromoted(PKG_P, UID_P)).isTrue();
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 36db955c3085..37bdf6b8614d 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -1111,9 +1111,9 @@ class KeyGestureControllerTests {
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
),
TestData(
- "FULLSCREEN -> Maximizes a task to fit the screen",
+ "FULLSCREEN -> Turns a task into fullscreen",
intArrayOf(KeyEvent.KEYCODE_FULLSCREEN),
- KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
intArrayOf(KeyEvent.KEYCODE_FULLSCREEN),
0,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index ec531275af1c..899cd7f9ce5e 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -180,7 +180,14 @@ def pack_script_to_uint32(script):
def dump_representative_locales(representative_locales):
"""Dump the set of representative locales."""
- print()
+ print('''
+/*
+ * TODO: Consider turning the below switch statement into binary search
+ * to save the disk space when the table is larger in the future.
+ * Disassembled code shows that the jump table emitted by clang can be
+ * 4x larger than the data in disk size, but it depends on the optimization option.
+ * However, a switch statement will benefit from the future of compiler improvement.
+ */''')
print('bool isLocaleRepresentative(uint32_t language_and_region, const char* script) {')
print(' const uint64_t packed_locale =')
print(' ((static_cast<uint64_t>(language_and_region)) << 32u) |')